diff --git a/.github/workflows/addon-checker.yml b/.github/workflows/addon-checker.yml index c6b940d8c..8f5e03518 100644 --- a/.github/workflows/addon-checker.yml +++ b/.github/workflows/addon-checker.yml @@ -22,7 +22,6 @@ jobs: run: | python -m pip install --upgrade pip pip3 install --user kodi-addon-checker - - name: Extract job variables shell: bash diff --git a/screensaver.digitalclock/README.md b/screensaver.digitalclock/README.md index d884b2d59..84fcd0a16 100644 --- a/screensaver.digitalclock/README.md +++ b/screensaver.digitalclock/README.md @@ -1,7 +1,7 @@ Digital clock screensaver ================= **Branches guide:** - - **master:** Works just on Kodi v19 Codename Matrix + - **master:** Works on Kodi v19 Codename Matrix and up - **krypton:** Works on Kodi v17 Codename Krypton and on Kodi V18 Codename Leia Digital clock screensaver with a lot of options. @@ -87,60 +87,33 @@ __Extra Options__ - Enable RSS (The screensaver will show the same RSS Kodi shows, so make sure it's properly configured and enabled in Kodi) This screensaver is configured for every skin separately since it has to use Fonts defined by the skin! - -Skin developers have an option to use script-screensaver-digitalclock-custom.xml in their skins 1080i, 720p... folder. -They should provide and maintain that xml file with their skin! -Screensaver will check for skin folders in this order: 1080i, 720p, 21x9, 16x9, 4x3Hirez. -If no script-screensaver-digitalclock-custom.xml is found screensaver will look for an appropriate xml file within screensavers folder. If there is no appropriate xml file it will use skin.default.xml -If the skin is not on the list screensaver will use default font names from confluence (It might not look pretty but it will work with any skin): - -- Ace2 -- Adonic -- Aeon Nox 5 +If the skin is not on the list below screensaver will use default font names from confluence (It might not look pretty but it will work with any skin). +List of supported skins (If the skin is in the official repository it will be supported): - Aeon Nox Silvo -- Aeon Tajo -- Aeon MQ5 -- Aeon MQ5 -- Aeon MQ6 -- Aeon MQ8 +- Aeon MQ7 (Matrix mod) +- Aeon MQ8 (Matrix mod, Nexus mod) +- Aeon MQ9 - Amber - AppTV -- Arctic: Zephyr -- Arctic: Zephyr 2 -- Aura -- Bello 6 -- Bello 7 -- Black Glass Nova -- Box -- Chroma +- Arctic: Zephyr - Reloaded +- Bello 8 - Confluence -- Embuary-Leia +- EllipsisUI +- Embuary-Matrix - Eminence.2 - Estouchy - Estuary - Ftv -- (Fuse)neue -- Grid -- Horizon -- Madnox - Metropolis -- Mimic -- Nebula -- Omni +- Mimic-LR - OSMC - Pellucid -- Phenomenal - Quartz - Rapier -- Retouched -- Reestuarized -- Revolve -- Titan -- Transparency +- TetradUI - Unity -- Xperience1080 If your skin is not on the list, and you would like it to be - send me a message. diff --git a/screensaver.digitalclock/addon.xml b/screensaver.digitalclock/addon.xml index e329443e4..7faace042 100644 --- a/screensaver.digitalclock/addon.xml +++ b/screensaver.digitalclock/addon.xml @@ -1,7 +1,7 @@ @@ -14,6 +14,8 @@ https://forum.kodi.tv/showthread.php?tid=237338 https://github.com/vdb86/screensaver.digitalclock + 6.0.5 (2024-1-2) - Added support for AeonMQ7 matrix mod, AeonMQ8 mods, for AeonMQ9, Arctic Zephyr - Reloaded, EllipsisUI, Embuary-Matrix, Mimic-LR, TetradUI, updated OSMC skin - thanks Ch1llb0 and petroid! Improved handling of no hour zero padding, added 2 more time formats, added support for turning off screen via CEC + 6.0.4 (2023-8-16) - Translations and a visual improvement for ftv skin - thanks Kevin! 6.0.3 (2021-9-7) - Bug fixes and translations 6.0.2 (2021-1-20) - Got skin.helper.backgrounds to work again 6.0.1 (2021-1-15) - Added support for several skins and fixed a bug @@ -26,32 +28,46 @@ resources/screenshot-2.png resources/screenshot-3.png + Pauseskærm med digitalt ur + Bildschirmschoner Digitaluhr Digital clock screensaver + Salvapantallas Reloj digital + Salvaschermo orologio digitale Wygaszacz ekranu jako zegar cyfrowy + Застава "Цифровые часы" Čuvar ekrana digitalni sat + 数字时钟屏幕保护程序 + Pauseskærm med digitalt ur og dato, afspilningsinformation, vejrinformation, billeddiasshow og flere indstillinger. + Bildschirmschoner mit Digitaluhr, Datum, Information über die aktuelle Wiedergabe, Wetter, Diashow und weitere Optionen. Digital clock screensaver with date, now playing information, weather information, image slideshow and several options. Digital clock screensaver with date, now playing information, weather information, image slideshow and several options. - Salvapantallas reloj digital con fecha, información de la reproducción actual, información meteorológica, imágenes y más opciones - Économiseur d'écran affichant une horloge numérique avec la date, des informations sur la lecture en cours, la météo, un diaporama d'images et plusieurs options. - Économiseur d'écran horloge numérique avec date, informations de lecture, informations météo, avec diaporama d'images et plusieurs options. - Screensaver orologio digitale con data, informazioni su elementi in riproduzione, meteo, presentazione immagini e altre opzioni. + Salvapantallas de reloj digital con fecha, información de reproducción actual, información meteorológica, presentación de imágenes y varias opciones. + Économiseur d'écran affichant une horloge numérique avec la date, des informations sur la lecture en cours, la météo, un diaporama d'images et plusieurs options. + Économiseur d'écran horloge numérique avec date, informations de lecture, informations météo, avec diaporama d'images et plusieurs options. + Salvaschermo orologio digitale con data, informazioni su elementi in riproduzione, meteo, presentazione immagini e altre opzioni. Skaitmeninio laikrodžio ekrano užsklanda su data, šiuo metu atkuriamo įrašo bei orų informacija, skaidrių demonstracija ar keletu kitų parinkčių Penyelamat skrin jam digital dengan tarikh, maklumat kini dimainkan, maklumat cuaca, paparan slaid imej dan beberapa pilihan lain. Wygaszacz ekranu wyświetlający cyfrowy zegar z datą, informacjami o aktualnie odtwarzanych mediach, o pogodzie, pokaz slajdów i kilkoma innymi opcjami. + Заставка цифровых часов, с датой, с информацией о погоде, со слайд-шоу и с несколькими опциями. Чувар екрана дигитални сат са датумом, подацима о тренутној репродукцији, подацима о временској прогнози, репродукцијом слајдова слика и неколико опција. Čuvar ekrana digitalni sat sa datumom, podacima o trenutnoj reprodukciji, podacima o vremenskoj prognozi, reprodukcijom slajdova slika i nekoliko opcija. Dijital saat ekran koruyucu, tarih ve saatin yanı sıra şimdi oynatılıyor bilgisi, hava durumu bilgisi, resim slayt gösterisi gibi çeşitli seçenekler sunar. + 数字时钟屏幕保护程序,带有日期、当前播放信息、天气信息、图片幻灯片和多个选项。 + Ved fejl, anmodninger eller generelle spørgsmål, besøg tråden Pauseskærm med digital ur på Kodi-forummet. + Für Fehler, Wünsche oder allgemeine Fragen den Thread „Digital clock screensaver“ im Kodi-Forum besuchen. For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum. For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum. - Para errores, peticiones o cuestiones generales visite el hilo Salvapantallas reloj digital en el foro de Kodi + Para errores, peticiones o cuestiones generales visite el hilo Salvapantallas reloj digital en el foro de Kodi. Concernant les bogues ou pour des questions générales, visitez le fil « Digital clock screensaver » du forum de Kodi. - Pour les bogues, demandes ou questions générales, merci de visiter le fil de discussion de l'économiseur d'écran horloge numérique, Digital clock screensaver, sur le forum Kodi. - Per bug, richieste o domande generali visita il thread dello Screensaver orologio digitale sul forum Kodi. + Pour les bogues, demandes ou questions générales, merci de visiter le fil de discussion de l'économiseur d'écran horloge numérique, Digital clock screensaver, sur le forum Kodi. + Per bug, richieste o domande generali visita il thread del salvaschermo orologio digitale sul forum Kodi. Norėdami pranešti apie klaidą, pateikti prašymą ar tiesiog paklausti, aplankykite skaitmeninio laikrodžio ekrano užsklandos temą Kodi forume. Jika ada pepijat, permintaan atau persoalan umum sila lawati bebenang Digital clock screensaver di dalam forum Kodi. Odwiedź dedykowany wygaszaczowi wątek na forum Kodi, aby zgłosić błędy, prośbę o funkcje albo zadać ogólne pytanie. + Для сообщений об ошибках, для пожеланий, для общих вопросов, посетите тему дополнения в форуме Kodi. За проблеме, захтеве или општа питања посетите Digital clock screensaver тему на Kodi форуму. Za probleme, zahteve ili opšta pitanja posetite Digital clock screensaver temu na Kodi forumu. Hata bildirme, istek ve genel sorularınız için Kodi forumunda dijital saat ekran koruyucu başlığını ziyaret edebilirsiniz. + 有关 bug、请求或一般问题,请访问 Kodi 论坛上的数字时钟屏幕保护程序讨论贴。 diff --git a/screensaver.digitalclock/default.py b/screensaver.digitalclock/default.py index 2dd6454a8..ca57a8401 100644 --- a/screensaver.digitalclock/default.py +++ b/screensaver.digitalclock/default.py @@ -74,6 +74,8 @@ def onInit(self): self.logoutcounter = 0 self.switch = 0 self.iconswitch = 0 + self.turnedoff = 0 + self.ceccounter = 0 self.movementtype = int(Addon.getSetting('movementtype')) self.movementspeed = int(Addon.getSetting('movementspeed')) self.stayinplace = int(Addon.getSetting('stayinplace')) @@ -134,6 +136,9 @@ def onInit(self): self.logout = Addon.getSetting('logout') self.logoutplaying = Addon.getSetting('logoutplaying') self.logouttime = int(Addon.getSetting('logouttime')) + self.cecoff = Addon.getSetting('cecoff') + self.cecoffplaying = Addon.getSetting('cecoffplaying') + self.cecofftime = int(Addon.getSetting('cecofftime')) self.rss = Addon.getSetting('rss') self.monitor = xbmc.Monitor() @@ -335,8 +340,8 @@ def onInit(self): self.icon_control.setImage(os.path.join(path,"resources/weathericons/",self.weathericonset[int(self.weathericonf)],xbmc.getInfoLabel('Window(Weather).Property(Current.FanartCode)')) + ".png") #setting up the time format - self.timeformat = ['%H','%I','%I','%#I','%#I','%-I','%-I'] - if self.timef == '2' or self.timef == '4' or self.timef == '6': + self.timeformat = ['%H','%I','%I','%#H','%#I','%#I','%-H','%-I','%-I'] + if self.timef == '2' or self.timef == '5' or self.timef == '8': self.ampm_control.setVisible(True) self.time = self.timeformat[int(self.timef)] @@ -450,18 +455,25 @@ def DisplayTime(self): if self.logout == 'true' and xbmc.getCondVisibility('Window.Previous(loginscreen)') == 0: self.logoutcounter +=1 if self.logoutcounter >= (self.multiplier*self.logouttime*60): - if xbmc.getCondVisibility('Player.HasMedia') == 1: - if self.logoutplaying == 'true': - xbmc.executebuiltin("PlayerControl(Stop)") - xbmc.log('Digital Clock Screensaver %s: Stopping media' %Addonversion) - xbmc.executebuiltin("System.LogOff") - xbmc.log('Digital Clock Screensaver %s: Logging out' %Addonversion) - self.logoutcounter = 0 - else: - xbmc.executebuiltin("System.LogOff") - xbmc.log('Digital Clock Screensaver %s: Logging out' %Addonversion) - self.logoutcounter = 0 - + if self.logoutplaying == 'true' and xbmc.getCondVisibility('Player.HasMedia') == 1: + xbmc.executebuiltin("PlayerControl(Stop)") + xbmc.log('Digital Clock Screensaver %s: Stopping media' %Addonversion) + xbmc.executebuiltin("System.LogOff") + xbmc.log('Digital Clock Screensaver %s: Logging out' %Addonversion) + self.logoutcounter = 0 + + #Turn off screen via CEC + if self.cecoff == 'true' and self.turnedoff == 0: + self.ceccounter +=1 + if self.ceccounter >= (self.multiplier*self.cecofftime*60): + if self.cecoffplaying == 'true' and xbmc.getCondVisibility('Player.HasMedia') == 1: + xbmc.executebuiltin("PlayerControl(Stop)") + xbmc.log('Digital Clock Screensaver %s: Stopping media' %Addonversion) + xbmc.executebuiltin("CECStandby") + xbmc.log('Digital Clock Screensaver %s: Turning screen off via CEC' %Addonversion) + self.ceccounter = 0 + self.turnedoff = 1 + self.monitor.waitForAbort(self.waittimer) def setCTR(self): @@ -506,7 +518,10 @@ def setCTR(self): self.shadowcolor = self.rtr + self.shadowcolor[2:] def Display(self): - self.hour_control.setLabel(datetime.now().strftime(self.time)) + if int(self.timef) == 4 or int(self.timef) == 5 or int(self.timef) == 7 or int(self.timef) == 8 or ((int(self.timef) == 3 or int(self.timef) == 6) and int(datetime.now().strftime(self.time))<10): + self.hour_control.setLabel(' ' + datetime.now().strftime(self.time)) + else: + self.hour_control.setLabel(datetime.now().strftime(self.time)) self.colon_control.setLabel(" : ") self.minute_control.setLabel(datetime.now().strftime("%M")) self.ampm_control.setLabel(datetime.now().strftime("%p")) diff --git a/screensaver.digitalclock/resources/language/resource.language.af_za/strings.po b/screensaver.digitalclock/resources/language/resource.language.af_za/strings.po index c678022cd..2b6ab4f90 100644 --- a/screensaver.digitalclock/resources/language/resource.language.af_za/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.af_za/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Afrikaans (South Africa) \n" "Language: af_za\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Stadig" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normaal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Vinnig" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.am_et/strings.po b/screensaver.digitalclock/resources/language/resource.language.am_et/strings.po index ef75c1cf8..fc5e927fa 100644 --- a/screensaver.digitalclock/resources/language/resource.language.am_et/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.am_et/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Amharic (Ethiopia) (http://www.transifex.com/projects/p/kodi-addons/language/am_ET/)\n" -"Language: am_ET\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Amharic (Ethiopia) \n" +"Language: am_et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "በዝግታ" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "መደበኛ" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "በፍጥነት" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -654,6 +664,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32310" #~ msgid "Hour color" #~ msgstr "የ ሰአት ቀለም" diff --git a/screensaver.digitalclock/resources/language/resource.language.ar_sa/strings.po b/screensaver.digitalclock/resources/language/resource.language.ar_sa/strings.po index a6530a972..5b2d86e5f 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ar_sa/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ar_sa/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Arabic (Saudi Arabia) \n" "Language: ar_sa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "بطيء" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "متوسط" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "سريع" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.az_az/strings.po b/screensaver.digitalclock/resources/language/resource.language.az_az/strings.po index 955705d9d..fde0994bd 100644 --- a/screensaver.digitalclock/resources/language/resource.language.az_az/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.az_az/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Azerbaijani \n" "Language: az_az\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.be_by/strings.po b/screensaver.digitalclock/resources/language/resource.language.be_by/strings.po index bc5f2fe78..e0f627d64 100644 --- a/screensaver.digitalclock/resources/language/resource.language.be_by/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.be_by/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-21 04:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Belarusian \n" "Language: be_by\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,11 +90,11 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Звычайны" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Хуткасць" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.bg_bg/strings.po b/screensaver.digitalclock/resources/language/resource.language.bg_bg/strings.po index 715deef6b..a4611e956 100644 --- a/screensaver.digitalclock/resources/language/resource.language.bg_bg/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.bg_bg/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Bulgarian \n" "Language: bg_bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Бавна" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Нормално" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Бърза" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.bs_ba/strings.po b/screensaver.digitalclock/resources/language/resource.language.bs_ba/strings.po index 992c31f9e..d9ac30198 100644 --- a/screensaver.digitalclock/resources/language/resource.language.bs_ba/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.bs_ba/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Bosnian (Bosnia and Herzegovina) \n" "Language: bs_ba\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,11 +90,11 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normalno" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Brzo" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ca_es/strings.po b/screensaver.digitalclock/resources/language/resource.language.ca_es/strings.po index 94ca97b37..ad6ac4110 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ca_es/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ca_es/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Catalan (Spain) \n" "Language: ca_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lent" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Ràpid" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.cs_cz/strings.po b/screensaver.digitalclock/resources/language/resource.language.cs_cz/strings.po index 8acfc4696..0d57bb045 100644 --- a/screensaver.digitalclock/resources/language/resource.language.cs_cz/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.cs_cz/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-09 13:42+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Czech \n" "Language: cs_cz\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Pomalu" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normální" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rychle" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.cy_gb/strings.po b/screensaver.digitalclock/resources/language/resource.language.cy_gb/strings.po index 307ce223b..cc5e5daa8 100644 --- a/screensaver.digitalclock/resources/language/resource.language.cy_gb/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.cy_gb/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Welsh (United Kingdom) \n" "Language: cy_gb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=(n==0) ? 0 : (n==1) ? 1 : (n==2) ? 2 : (n==3) ? 3 :(n==6) ? 4 : 5;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Araf" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Cyflym" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.da_dk/strings.po b/screensaver.digitalclock/resources/language/resource.language.da_dk/strings.po index 95d237867..fc05bc653 100644 --- a/screensaver.digitalclock/resources/language/resource.language.da_dk/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.da_dk/strings.po @@ -7,93 +7,94 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-02-14 09:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Danish \n" "Language: da_dk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.10.1\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "Pauseskærm med digitalt ur" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "" +msgstr "Pauseskærm med digitalt ur og dato, afspilningsinformation, vejrinformation, billeddiasshow og flere indstillinger." msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "" +msgstr "Ved fejl, anmodninger eller generelle spørgsmål, besøg tråden Pauseskærm med digital ur på Kodi-forummet." # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "Bevægelse" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "Format" # empty strings from id 32002 to 32004 msgctxt "#32005" msgid "Additional Information" -msgstr "" +msgstr "Yderligere information" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "Farve | Gennemsigtighed" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "Baggrund" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "Ekstra indstillinger" # empty strings from id 32026 to 32099 msgctxt "#32100" msgid "Movement on the screen" -msgstr "" +msgstr "Bevægelse på skærmen" msgctxt "#32101" msgid "Random position" -msgstr "" +msgstr "Tilfældig position" msgctxt "#32102" msgid "Bounce" -msgstr "" +msgstr "Hop" msgctxt "#32103" msgid "Fixed in the center" -msgstr "" +msgstr "Fast på midten" msgctxt "#32104" msgid "Custom fixed position" -msgstr "" +msgstr "Brugerdefineret fast position" # empty strings from id 32105 to 32109 msgctxt "#32110" msgid "Movement speed" -msgstr "" +msgstr "Bevægelseshastighed" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Langsom" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Hurtig" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -102,24 +103,24 @@ msgstr "" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" -msgstr "" +msgstr "Advarsel! Statisk tekst på din skærm kan forårsage indbrændinger!" # empty strings from id 32122 to 32129 msgctxt "#32130" msgid "Time format" -msgstr "" +msgstr "Tidsformat" msgctxt "#32131" msgid "17:14" -msgstr "" +msgstr "17:14" msgctxt "#32132" msgid "05:14" -msgstr "" +msgstr "05:14" msgctxt "#32133" msgid "05:14 PM" -msgstr "" +msgstr "05:14 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.de_de/strings.po b/screensaver.digitalclock/resources/language/resource.language.de_de/strings.po index ac6f9d7c5..b6e50ed0d 100644 --- a/screensaver.digitalclock/resources/language/resource.language.de_de/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.de_de/strings.po @@ -7,649 +7,671 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2023-02-06 14:15+0000\n" +"Last-Translator: Demian \n" +"Language-Team: German \n" "Language: de_de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "Bildschirmschoner Digitaluhr" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "" +msgstr "Bildschirmschoner mit Digitaluhr, Datum, Information über die aktuelle Wiedergabe, Wetter, Diashow und weitere Optionen." msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "" +msgstr "Für Fehler, Wünsche oder allgemeine Fragen den Thread „Digital clock screensaver“ im Kodi-Forum besuchen." # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "Bewegung" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "Format" # empty strings from id 32002 to 32004 msgctxt "#32005" msgid "Additional Information" -msgstr "" +msgstr "Zusätzliche information" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "Farbe | Transparenz" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "Hintergrund" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "Weitere Einstellungen" # empty strings from id 32026 to 32099 msgctxt "#32100" msgid "Movement on the screen" -msgstr "" +msgstr "Bewegung auf dem Bildschirm" msgctxt "#32101" msgid "Random position" -msgstr "" +msgstr "Zufällige Positionierung" msgctxt "#32102" msgid "Bounce" -msgstr "" +msgstr "Hüpfen" msgctxt "#32103" msgid "Fixed in the center" -msgstr "" +msgstr "In der Mitte fixiert" msgctxt "#32104" msgid "Custom fixed position" -msgstr "" +msgstr "Benutzerdefinierte feste Position" # empty strings from id 32105 to 32109 msgctxt "#32110" msgid "Movement speed" -msgstr "" +msgstr "Bewegungsgeschwindigkeit" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Langsam" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Schnell" # empty strings from id 32114 to 32119 msgctxt "#32120" msgid "Number of seconds to stay in place" -msgstr "" +msgstr "Anzahl der Sekunden an gleicher Stelle" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" -msgstr "" +msgstr "Warnung! Statischer Text kann auf dem Bildschirm einbrennen!" # empty strings from id 32122 to 32129 msgctxt "#32130" msgid "Time format" -msgstr "" +msgstr "Zeitformat" msgctxt "#32131" msgid "17:14" -msgstr "" +msgstr "17:14" msgctxt "#32132" msgid "05:14" -msgstr "" +msgstr "05:14" msgctxt "#32133" msgid "05:14 PM" -msgstr "" +msgstr "05:15 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" -msgstr "" +msgstr "5:14 Keine Nullenauffüllung für Stunden, Windows" msgctxt "#32135" msgid "5:14 PM No hour zero padding Windows" -msgstr "" +msgstr "5:14 PM Keine Nullenauffüllung für Stunde, Windows" msgctxt "#32136" msgid "5:14 No hour zero padding Unix" -msgstr "" +msgstr "5:14 Keine Nullenauffüllung für Stunde, Unix" msgctxt "#32137" msgid "5:14 PM No hour zero padding Unix" -msgstr "" +msgstr "5:14 PM Keine Nullenauffüllung für Stunde, Unix" # empty strings from id 32138 to 32138 msgctxt "#32139" msgid "Colon blink" -msgstr "" +msgstr "Blinkendes Trennzeichen" msgctxt "#32140" msgid "Date format" -msgstr "" +msgstr "Datumsformat" msgctxt "#32141" msgid "Hide date" -msgstr "" +msgstr "Datum ausblenden" msgctxt "#32142" msgid "Saturday 30. May 2015" -msgstr "" +msgstr "Samstag 30. Mai 2015" msgctxt "#32143" msgid "30.05.2015" -msgstr "" +msgstr "30.05.2015" msgctxt "#32144" msgid "05.30.2015" -msgstr "" +msgstr "05.30.2015" msgctxt "#32145" msgid "Custom" -msgstr "" +msgstr "Benutzerdefiniert" # empty strings from id 32146 to 32149 msgctxt "#32150" msgid "Custom value for X position (1-1280)" -msgstr "" +msgstr "Benutzerdefinierter Wert für X-Position (1–1280)" # empty strings from id 32151 to 32159 msgctxt "#32160" msgid "Custom value for Y position (1-720)" -msgstr "" +msgstr "Benutzerdefinierter Wert für Y-Position (1–720)" # empty strings from id 32161 to 32169 msgctxt "#32170" msgid "Enter custom date format (Empty=Regional settings):" -msgstr "" +msgstr "Benutzerdefiniertes Datumsformat eingeben (Leer = Übernahme der regionalen Einstellungen):" msgctxt "#32171" msgid "Day: d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" -msgstr "" +msgstr "Tag: d(1–31) dd(01–31) ddd(Mo–So) DDD(Montag–Sonntag)" msgctxt "#32172" msgid "Month: m(1-12) mm(01-12) mmm(Jan-Dec) MMM(January-December)" -msgstr "" +msgstr "Monat: m(1–12) mm(01–12) mmm(Jan–Dez) MMM(Januar–Dezember)" msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" +msgstr "Jahr: yy(2-stelliges Jahr) yyyy(4-stelliges Jahr)" + +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" msgstr "" # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" -msgstr "" +msgstr "Zusätzliche Informationen aktivieren" # empty strings from id 32201 to 32209 msgctxt "#32210" msgid "Number of seconds for information switch" -msgstr "" +msgstr "Anzahl der Sekunden für den Informationswechsel" # empty strings from id 32211 to 32219 msgctxt "#32220" msgid "Enable now playing information" -msgstr "" +msgstr "Information „gerade läuft“ aktivieren" msgctxt "#32221" msgid "Show only music information" -msgstr "" +msgstr "Nur Musikinformationen anzeigen" msgctxt "#32222" msgid "This will hide all other info while the music is playing!" -msgstr "" +msgstr "Dadurch werden alle anderen Informationen ausgeblendet, während die Musik läuft!" msgctxt "#32223" msgid "Merge song artist and title" -msgstr "" +msgstr "Songinterpret und Titel zusammenführen" # empty strings from id 32224 to 32229 msgctxt "#32230" msgid "Enable weather information" -msgstr "" +msgstr "Wetterinformation aktivieren" # empty strings from id 32231 to 32239 msgctxt "#32240" msgid "Weather icons" -msgstr "" +msgstr "Wettersymbole" msgctxt "#32241" msgid "Hide weather icon" -msgstr "" +msgstr "Wettersymbole ausblenden" msgctxt "#32242" msgid "Icon set 1" -msgstr "" +msgstr "Symbolsatz 1" msgctxt "#32243" msgid "Icon set 2" -msgstr "" +msgstr "Symbolsatz 2" msgctxt "#32244" msgid "Icon set 3" -msgstr "" +msgstr "Symbolsatz 3" msgctxt "#32245" msgid "Icon set 4" -msgstr "" +msgstr "Symbolsatz 4" # empty strings from id 32246 to 32249 msgctxt "#32250" msgid "Enable album art" -msgstr "" +msgstr "Albumart aktivieren" # empty strings from id 32251 to 32259 msgctxt "#32260" msgid "Enable CPU usage information" -msgstr "" +msgstr "CPU-Auslastungsinformationen aktivieren" msgctxt "#32261" msgid "Enable battery level information" -msgstr "" +msgstr "Informationen zum Batteriestand aktivieren" msgctxt "#32262" msgid "Enable free memory information" -msgstr "" +msgstr "Informationen über freien Speicher aktivieren" msgctxt "#32263" msgid "Enable movie library information" -msgstr "" +msgstr "Informationen zur Filmbibliothek aktivieren" msgctxt "#32264" msgid "Enable TV show library information" -msgstr "" +msgstr "Informationen zur Serienbibliothek aktivieren" msgctxt "#32265" msgid "Enable music library information" -msgstr "" +msgstr "Informationen zur Musikbibliothek aktivieren" msgctxt "#32266" msgid "Enable CPU temperature" -msgstr "" +msgstr "CPU-Temperatur aktivieren" msgctxt "#32267" msgid "Enable GPU temperature" -msgstr "" +msgstr "GPU-Temperatur aktivieren" msgctxt "#32268" msgid "Enable HDD temperature" -msgstr "" +msgstr "HDD-Temperatur aktivieren" msgctxt "#32269" msgid "Enable FPS" -msgstr "" +msgstr "FPS aktivieren" msgctxt "#32270" msgid "Enable current uptime" -msgstr "" +msgstr "Aktuelle Betriebszeit aktivieren" msgctxt "#32271" msgid "Enable total uptime" -msgstr "" +msgstr "Gesamtbetriebszeit aktivieren" # empty strings from id 32272 to 32279 msgctxt "#32280" msgid "CPU:" -msgstr "" +msgstr "CPU:" msgctxt "#32281" msgid "Battery:" -msgstr "" +msgstr "Batterie:" msgctxt "#32282" msgid "Free memory:" -msgstr "" +msgstr "Freier Speicher:" msgctxt "#32283" msgid "Total movies:" -msgstr "" +msgstr "Filme insgesamt:" msgctxt "#32284" msgid "Watched movies:" -msgstr "" +msgstr "Gesehene Filme:" msgctxt "#32285" msgid "Unwatched movies:" -msgstr "" +msgstr "Ungesehene Filme:" msgctxt "#32286" msgid "Total TV shows:" -msgstr "" +msgstr "Serien insgesamt:" msgctxt "#32287" msgid "Total episodes:" -msgstr "" +msgstr "Episoden insgesamt:" msgctxt "#32288" msgid "Watched TV shows:" -msgstr "" +msgstr "Gesehene Serien:" msgctxt "#32289" msgid "Watched episodes:" -msgstr "" +msgstr "Gesehene Episoden:" msgctxt "#32290" msgid "Unwatched TV shows:" -msgstr "" +msgstr "Ungesehene Serien:" msgctxt "#32291" msgid "Unwatched episodes:" -msgstr "" +msgstr "Ungesehene Episoden:" msgctxt "#32292" msgid "Artists:" -msgstr "" +msgstr "Interpreten:" msgctxt "#32293" msgid "Albums:" -msgstr "" +msgstr "Alben:" msgctxt "#32294" msgid "Songs:" -msgstr "" +msgstr "Titel:" msgctxt "#32295" msgid "CPU temperature:" -msgstr "" +msgstr "CPU-Temperatur:" msgctxt "#32296" msgid "GPU temperature:" -msgstr "" +msgstr "GPU-Temperatur:" msgctxt "#32297" msgid "HDD temperature:" -msgstr "" +msgstr "HDD-Temperatur:" msgctxt "#32298" msgid "FPS:" -msgstr "" +msgstr "FPS:" msgctxt "#32299" msgid "Current uptime:" -msgstr "" +msgstr "Aktuelle Betriebszeit:" msgctxt "#32300" msgid "Total uptime:" -msgstr "" +msgstr "Gesamtbetriebszeit:" # empty strings from id 32301 to 32309 msgctxt "#32310" msgid "Choose hour color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" -msgstr "" +msgstr "Stundenfarbe und -deckkraft auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" msgctxt "#32311" msgid "Random hour color" -msgstr "" +msgstr "Zufällige Stundenfarbe" msgctxt "#32312" msgid "Random hour opacity" -msgstr "" +msgstr "Zufällige Stundendeckkraft" # empty strings from id 32313 to 32319 msgctxt "#32320" msgid "Choose colon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" -msgstr "" +msgstr "Farbe und Deckkraft des Doppelpunkts auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" msgctxt "#32321" msgid "Random colon color" -msgstr "" +msgstr "Zufällige Doppelpunktfarbe" msgctxt "#32322" msgid "Random colon opacity" -msgstr "" +msgstr "Zufällige Doppelpunktdeckkraft" # empty strings from id 32323 to 32329 msgctxt "#32330" msgid "Choose minute color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" -msgstr "" +msgstr "Minutenfarbe und -deckkraft auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" msgctxt "#32331" msgid "Random minute color" -msgstr "" +msgstr "Zufälliger Minutenfarbe" msgctxt "#32332" msgid "Random minute opacity" -msgstr "" +msgstr "Zufälliger Minutendeckkraft" # empty strings from id 32333 to 32339 msgctxt "#32340" msgid "Choose AM/PM color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" -msgstr "" +msgstr "AM/PM-Farbe und -deckkraft auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" msgctxt "#32341" msgid "Random AM/PM color" -msgstr "" +msgstr "Zufällige AM/PM-Farbe" msgctxt "#32342" msgid "Random AM/PM opacity" -msgstr "" +msgstr "Zufällige AM/PM-Deckkraft" # empty strings from id 32343 to 32349 msgctxt "#32350" msgid "Choose date color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" -msgstr "" +msgstr "Farbe und Deckkraft des Datums auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" msgctxt "#32351" msgid "Random date color" -msgstr "" +msgstr "Zufällige Datumsfarbe" msgctxt "#32352" msgid "Random date opacity" -msgstr "" +msgstr "Zufällige Datumsdeckkraft" # empty strings from id 32353 to 32359 msgctxt "#32360" msgid "Choose additional information color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" -msgstr "" +msgstr "Farbe und Deckkraft der Zusatzinformationen auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" msgctxt "#32361" msgid "Random additional information color" -msgstr "" +msgstr "Zufällige Farbe für zusätzliche Informationen" msgctxt "#32362" msgid "Random additional information opacity" -msgstr "" +msgstr "Zufällige Deckkraft für zusätzliche Informationen" # empty strings from id 32363 to 32369 msgctxt "#32370" msgid "Choose icon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" -msgstr "" +msgstr "Farbe und Deckkraft des Symbols auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" msgctxt "#32371" msgid "Random icon color" -msgstr "" +msgstr "Zufällige Symbolfarbe" msgctxt "#32372" msgid "Random icon opacity" -msgstr "" +msgstr "Zufällige Symboldeckkraft" # empty strings from id 32373 to 32499 msgctxt "#32500" msgid "Choose text shadow color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" -msgstr "" +msgstr "Textschattenfarbe und -deckkraft auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" # empty strings from id 32501 to 32504 msgctxt "#32505" msgid "Choose background image aspect ratio:" -msgstr "" +msgstr "Seitenverhältnis des Hintergrundbildes wählen:" msgctxt "#32506" msgid "Fill the screen" -msgstr "" +msgstr "Bildschirm füllen" msgctxt "#32507" msgid "Original size" -msgstr "" +msgstr "Originalgröße" # empty strings from id 32508 to 32509 msgctxt "#32510" msgid "Choose background" -msgstr "" +msgstr "Hintergrund wählen" msgctxt "#32511" msgid "One color" -msgstr "" +msgstr "Eine Farbe" msgctxt "#32512" msgid "Single image" -msgstr "" +msgstr "Einzelbild" msgctxt "#32513" msgid "Slideshow" -msgstr "" +msgstr "Diashow" msgctxt "#32514" msgid "Skin Helper Backgrounds" -msgstr "" +msgstr "Skin-Helper-Hintergründe" msgctxt "#32515" msgid "Dim" -msgstr "" +msgstr "Dimmen" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "Helligkeitsstufe" # empty strings from id 32517 to 32519 msgctxt "#32520" msgid "Choose background color - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" -msgstr "" +msgstr "Hintergrundfarbe auswählen - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" # empty strings from id 32521 to 32529 msgctxt "#32530" msgid "Choose image" -msgstr "" +msgstr "Bild wählen" msgctxt "#32531" msgid "Warning! Having only one image on your screen can cause a burn in!" -msgstr "" +msgstr "Warnung! Wenn nur ein Bild auf dem Bildschirm angezeigt wird, kann es zu einem Einbrennen kommen!" # empty strings from id 32532 to 32539 msgctxt "#32540" msgid "Choose slideshow directory" -msgstr "" +msgstr "Diashowverzeichnis auswählen" # empty strings from id 32541 to 32549 msgctxt "#32550" msgid "Random images" -msgstr "" +msgstr "Zufällige Bilder" # empty strings from id 32551 to 32559 msgctxt "#32560" msgid "Change background picture every:" -msgstr "" +msgstr "Hintergrundbild wechseln alle:" msgctxt "#32561" msgid "15 sec" -msgstr "" +msgstr "15 s" msgctxt "#32562" msgid "30 sec" -msgstr "" +msgstr "30 s" msgctxt "#32563" msgid "1 min" -msgstr "" +msgstr "1 Min" msgctxt "#32564" msgid "2 min" -msgstr "" +msgstr "2 Min" msgctxt "#32565" msgid "3 min" -msgstr "" +msgstr "3 Min" msgctxt "#32566" msgid "4 min" -msgstr "" +msgstr "4 Min" msgctxt "#32567" msgid "5 min" -msgstr "" +msgstr "5 Min" msgctxt "#32568" msgid "6 min" -msgstr "" +msgstr "6 Min" msgctxt "#32569" msgid "7 min" -msgstr "" +msgstr "7 Min" msgctxt "#32570" msgid "8 min" -msgstr "" +msgstr "8 Min" msgctxt "#32571" msgid "9 min" -msgstr "" +msgstr "9 Min" msgctxt "#32572" msgid "10 min" -msgstr "" +msgstr "10 Min" # empty strings from id 32573 to 32578 msgctxt "#32579" msgid "Requires skin.helper.backgrounds!" -msgstr "" +msgstr "Erfordert skin.helper.backgrounds!" msgctxt "#32580" msgid "Choose Skin Helper background" -msgstr "" +msgstr "Skin-Helper-Hintergrund auswählen" msgctxt "#32581" msgid "Movie random fanart" -msgstr "" +msgstr "Zufälliges Filmfanart" msgctxt "#32582" msgid "TV show random fanart" -msgstr "" +msgstr "Zufälliges Serienfanart" msgctxt "#32583" msgid "Music artist random fanart" -msgstr "" +msgstr "Zufälliges Musikinterpret-Fanart" msgctxt "#32584" msgid "Random fanart of all media types" -msgstr "" +msgstr "Zufälliges Fanart für alle Medientypen" # empty strings from id 32585 to 32599 msgctxt "#32600" msgid "Element size increase (%)" -msgstr "" +msgstr "Vergrößerung des Elements (%)" msgctxt "#32610" msgid "Log out" -msgstr "" +msgstr "Abmelden" msgctxt "#32611" msgid "Stop now playing media" -msgstr "" +msgstr "Medienwiedergabe jetzt stoppen" msgctxt "#32612" msgid "Log out after (minutes)" -msgstr "" +msgstr "Abmelden nach (Minuten)" msgctxt "#32620" msgid "Enable RSS" -msgstr "" +msgstr "RSS aktivieren" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" +msgstr "RSS muss in Kodi richtig konfiguriert und aktiviert sein!" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.el_gr/strings.po b/screensaver.digitalclock/resources/language/resource.language.el_gr/strings.po index 5c3b53f89..17fad3a48 100644 --- a/screensaver.digitalclock/resources/language/resource.language.el_gr/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.el_gr/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Greek \n" "Language: el_gr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Αργά" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Κανονική" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Γρήγορα" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.en_au/strings.po b/screensaver.digitalclock/resources/language/resource.language.en_au/strings.po index deae52795..c9b1a5e07 100644 --- a/screensaver.digitalclock/resources/language/resource.language.en_au/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.en_au/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-09 13:42+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: English (Australia) \n" "Language: en_au\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.en_gb/strings.po b/screensaver.digitalclock/resources/language/resource.language.en_gb/strings.po index 391bd1404..a4080cb2f 100644 --- a/screensaver.digitalclock/resources/language/resource.language.en_gb/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.en_gb/strings.po @@ -206,7 +206,17 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" -#empty strings from id 32174 to 32199 +#empty strings from id 32174 to 32179 + +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + +#empty strings from id 32182 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -691,3 +701,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" \ No newline at end of file diff --git a/screensaver.digitalclock/resources/language/resource.language.en_nz/strings.po b/screensaver.digitalclock/resources/language/resource.language.en_nz/strings.po index f0d487a12..dd01d35f6 100644 --- a/screensaver.digitalclock/resources/language/resource.language.en_nz/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.en_nz/strings.po @@ -185,6 +185,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Enable additional information" @@ -635,6 +644,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Colours" diff --git a/screensaver.digitalclock/resources/language/resource.language.en_us/strings.po b/screensaver.digitalclock/resources/language/resource.language.en_us/strings.po index 270af4866..ffe01bed9 100644 --- a/screensaver.digitalclock/resources/language/resource.language.en_us/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.en_us/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-10 15:00+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: English (United States) \n" "Language: en_us\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Slow" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Fast" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.eo/strings.po b/screensaver.digitalclock/resources/language/resource.language.eo/strings.po index 07535667f..906f5a606 100644 --- a/screensaver.digitalclock/resources/language/resource.language.eo/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.eo/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Esperanto \n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.es_ar/strings.po b/screensaver.digitalclock/resources/language/resource.language.es_ar/strings.po index bdc8e211b..fc05fa12b 100644 --- a/screensaver.digitalclock/resources/language/resource.language.es_ar/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.es_ar/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Spanish (Argentina) \n" "Language: es_ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lenta" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rápida" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.es_es/strings.po b/screensaver.digitalclock/resources/language/resource.language.es_es/strings.po index e46a50e6f..b599dccee 100644 --- a/screensaver.digitalclock/resources/language/resource.language.es_es/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.es_es/strings.po @@ -5,38 +5,38 @@ msgid "" msgstr "" "Project-Id-Version: KODI Addons\n" -"Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2021-08-24 09:29+0000\n" -"Last-Translator: Christian Gade \n" +"PO-Revision-Date: 2024-01-12 00:13+0000\n" +"Last-Translator: José Antonio Alvarado \n" "Language-Team: Spanish (Spain) \n" "Language: es_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.8\n" +"X-Generator: Weblate 5.3\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "Salvapantallas Reloj digital" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "Salvapantallas reloj digital con fecha, información de la reproducción actual, información meteorológica, imágenes y más opciones" +msgstr "Salvapantallas de reloj digital con fecha, información de reproducción actual, información meteorológica, presentación de imágenes y varias opciones." msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "Para errores, peticiones o cuestiones generales visite el hilo Salvapantallas reloj digital en el foro de Kodi" +msgstr "Para errores, peticiones o cuestiones generales visite el hilo Salvapantallas reloj digital en el foro de Kodi." # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "Movimiento" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "Formato" msgctxt "#32005" msgid "Additional Information" @@ -45,16 +45,16 @@ msgstr "Información adicional" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "Color | Opacidad" msgctxt "#32020" msgid "Background" -msgstr "Fondo" +msgstr "Segundo plano" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "Opciones adicionales" msgctxt "#32100" msgid "Movement on the screen" @@ -98,7 +98,7 @@ msgstr "Número de segundos que permanecerá en el sitio" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" -msgstr "¡Atención! ¡Tener un texto estático en tu pantalla puede quemarla!" +msgstr "¡Atención! ¡Un texto estático en la pantalla puede quemarla!" msgctxt "#32130" msgid "Time format" @@ -118,24 +118,24 @@ msgstr "05:14 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" -msgstr "" +msgstr "5:14 Sin relleno de horas con cero en Windows" msgctxt "#32135" msgid "5:14 PM No hour zero padding Windows" -msgstr "" +msgstr "5:14 PM Sin relleno de horas con cero en Windows" msgctxt "#32136" msgid "5:14 No hour zero padding Unix" -msgstr "" +msgstr "5:14 Sin relleno de horas con cero en Unix" msgctxt "#32137" msgid "5:14 PM No hour zero padding Unix" -msgstr "" +msgstr "5:14 PM Sin relleno de horas con cero en Unix" # empty strings from id 32138 to 32138 msgctxt "#32139" msgid "Colon blink" -msgstr "" +msgstr "Parpadeo de los dos puntos" msgctxt "#32140" msgid "Date format" @@ -159,32 +159,41 @@ msgstr "05.30.2015" msgctxt "#32145" msgid "Custom" -msgstr "" +msgstr "Personalizar" msgctxt "#32150" msgid "Custom value for X position (1-1280)" -msgstr "Posición X (1-1280)" +msgstr "Valor personalizado para la posición X (1-1280)" msgctxt "#32160" msgid "Custom value for Y position (1-720)" -msgstr "Posición Y (1-720)" +msgstr "Valor personalizado para la posición Y (1-720)" # empty strings from id 32161 to 32169 msgctxt "#32170" msgid "Enter custom date format (Empty=Regional settings):" -msgstr "" +msgstr "Introducir formato de fecha personalizado (Vacío=Configuración regional):" msgctxt "#32171" msgid "Day: d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" -msgstr "" +msgstr "Día: d(1-31) dd(01-31) ddd(Lun-Dom) DDD(Lunes-Domingo)" msgctxt "#32172" msgid "Month: m(1-12) mm(01-12) mmm(Jan-Dec) MMM(January-December)" -msgstr "" +msgstr "Mes: m(1-12) mm(01-12) mmm(Ene-Dic) MMM(Enero-Diciembre)" msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" -msgstr "" +msgstr "Año: yy(año en 2 dígitos) yyyy(año en 4 dígitos)" + +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "17:14 Sin relleno de ceros para la hora en Windows" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "17:14 Sin relleno de ceros para la hora en Unix" msgctxt "#32200" msgid "Enable additional information" @@ -196,31 +205,31 @@ msgstr "Número de segundos para el cambio de información" msgctxt "#32220" msgid "Enable now playing information" -msgstr "Activar información Reproduciendo ahora" +msgstr "Activar información de reproducción en curso" msgctxt "#32221" msgid "Show only music information" -msgstr "" +msgstr "Mostrar sólo información de música" msgctxt "#32222" msgid "This will hide all other info while the music is playing!" -msgstr "" +msgstr "¡Esto ocultará el resto de la información mientras se reproduce la música!" msgctxt "#32223" msgid "Merge song artist and title" -msgstr "" +msgstr "Combinar artista y título de canción" msgctxt "#32230" msgid "Enable weather information" -msgstr "Activar información del tiempo" +msgstr "Activar información meteorológica" msgctxt "#32240" msgid "Weather icons" -msgstr "Iconos del tiempo" +msgstr "Iconos meteorológicos" msgctxt "#32241" msgid "Hide weather icon" -msgstr "Ocultar iconos del tiempo" +msgstr "Ocultar el icono del tiempo" msgctxt "#32242" msgid "Icon set 1" @@ -240,250 +249,250 @@ msgstr "Conjunto de iconos 4" msgctxt "#32250" msgid "Enable album art" -msgstr "Activar carátula de álbum" +msgstr "Habilitar carátula de álbum" # empty strings from id 32251 to 32259 msgctxt "#32260" msgid "Enable CPU usage information" -msgstr "" +msgstr "Habilitar información del uso de la CPU" msgctxt "#32261" msgid "Enable battery level information" -msgstr "" +msgstr "Habilitar información del nivel de batería" msgctxt "#32262" msgid "Enable free memory information" -msgstr "" +msgstr "Habilitar información de memoria libre" msgctxt "#32263" msgid "Enable movie library information" -msgstr "" +msgstr "Habilitar información de la biblioteca de películas" msgctxt "#32264" msgid "Enable TV show library information" -msgstr "" +msgstr "Habilitar información de la biblioteca de programas de TV" msgctxt "#32265" msgid "Enable music library information" -msgstr "" +msgstr "Habilitar información de la biblioteca de música" msgctxt "#32266" msgid "Enable CPU temperature" -msgstr "" +msgstr "Habilitar temperatura de la CPU" msgctxt "#32267" msgid "Enable GPU temperature" -msgstr "" +msgstr "Habilitar temperatura de la GPU" msgctxt "#32268" msgid "Enable HDD temperature" -msgstr "" +msgstr "Habilitar temperatura del disco duro" msgctxt "#32269" msgid "Enable FPS" -msgstr "" +msgstr "Habilitar FPS" msgctxt "#32270" msgid "Enable current uptime" -msgstr "" +msgstr "Habilitar tiempo de actividad actual" msgctxt "#32271" msgid "Enable total uptime" -msgstr "" +msgstr "Habilitar tiempo de actividad total" # empty strings from id 32272 to 32279 msgctxt "#32280" msgid "CPU:" -msgstr "" +msgstr "CPU:" msgctxt "#32281" msgid "Battery:" -msgstr "" +msgstr "Batería:" msgctxt "#32282" msgid "Free memory:" -msgstr "" +msgstr "Memoria libre:" msgctxt "#32283" msgid "Total movies:" -msgstr "" +msgstr "Total películas:" msgctxt "#32284" msgid "Watched movies:" -msgstr "" +msgstr "Películas vistas:" msgctxt "#32285" msgid "Unwatched movies:" -msgstr "" +msgstr "Películas sin ver:" msgctxt "#32286" msgid "Total TV shows:" -msgstr "" +msgstr "Total programas de TV:" msgctxt "#32287" msgid "Total episodes:" -msgstr "" +msgstr "Total episodios:" msgctxt "#32288" msgid "Watched TV shows:" -msgstr "" +msgstr "Programas de TV vistos:" msgctxt "#32289" msgid "Watched episodes:" -msgstr "" +msgstr "Episodios vistos:" msgctxt "#32290" msgid "Unwatched TV shows:" -msgstr "" +msgstr "Programas de TV sin ver:" msgctxt "#32291" msgid "Unwatched episodes:" -msgstr "" +msgstr "Episodios sin ver:" msgctxt "#32292" msgid "Artists:" -msgstr "" +msgstr "Artistas:" msgctxt "#32293" msgid "Albums:" -msgstr "" +msgstr "Álbumes:" msgctxt "#32294" msgid "Songs:" -msgstr "" +msgstr "Canciones:" msgctxt "#32295" msgid "CPU temperature:" -msgstr "" +msgstr "Temperatura de la CPU:" msgctxt "#32296" msgid "GPU temperature:" -msgstr "" +msgstr "Temperatura de la GPU:" msgctxt "#32297" msgid "HDD temperature:" -msgstr "" +msgstr "Temperatura del disco duro:" msgctxt "#32298" msgid "FPS:" -msgstr "" +msgstr "FPS:" msgctxt "#32299" msgid "Current uptime:" -msgstr "" +msgstr "Tiempo de actividad actual:" msgctxt "#32300" msgid "Total uptime:" -msgstr "" +msgstr "Tiempo de actividad total:" # empty strings from id 32301 to 32309 msgctxt "#32310" msgid "Choose hour color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de la hora y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" msgctxt "#32311" msgid "Random hour color" -msgstr "" +msgstr "Color de la hora aleatorio" msgctxt "#32312" msgid "Random hour opacity" -msgstr "" +msgstr "Opacidad de la hora aleatoria" # empty strings from id 32313 to 32319 msgctxt "#32320" msgid "Choose colon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de los dos puntos y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" msgctxt "#32321" msgid "Random colon color" -msgstr "" +msgstr "Color aleatorio de los dos puntos" msgctxt "#32322" msgid "Random colon opacity" -msgstr "" +msgstr "Opacidad aleatoria de los dos puntos" # empty strings from id 32323 to 32329 msgctxt "#32330" msgid "Choose minute color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color del minuto y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" msgctxt "#32331" msgid "Random minute color" -msgstr "" +msgstr "Color aleatorio de minutos" msgctxt "#32332" msgid "Random minute opacity" -msgstr "" +msgstr "Opacidad aleatoria de minutos" # empty strings from id 32333 to 32339 msgctxt "#32340" msgid "Choose AM/PM color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de AM/PM y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" msgctxt "#32341" msgid "Random AM/PM color" -msgstr "" +msgstr "Color aleatorio AM/PM" msgctxt "#32342" msgid "Random AM/PM opacity" -msgstr "" +msgstr "Opacidad aleatoria AM/PM" # empty strings from id 32343 to 32349 msgctxt "#32350" msgid "Choose date color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de la fecha y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" msgctxt "#32351" msgid "Random date color" -msgstr "" +msgstr "Color aleatorio de la fecha" msgctxt "#32352" msgid "Random date opacity" -msgstr "" +msgstr "Opacidad aleatoria de la fecha" # empty strings from id 32353 to 32359 msgctxt "#32360" msgid "Choose additional information color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de la información adicional y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" msgctxt "#32361" msgid "Random additional information color" -msgstr "" +msgstr "Color aleatorio de la información adicional" msgctxt "#32362" msgid "Random additional information opacity" -msgstr "" +msgstr "Opacidad aleatoria de la información adicional" # empty strings from id 32363 to 32369 msgctxt "#32370" msgid "Choose icon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color del icono y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" msgctxt "#32371" msgid "Random icon color" -msgstr "" +msgstr "Color aleatorio del icono" msgctxt "#32372" msgid "Random icon opacity" -msgstr "" +msgstr "Opacidad aleatoria del icono" # empty strings from id 32373 to 32499 msgctxt "#32500" msgid "Choose text shadow color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" -msgstr "" +msgstr "Elegir el color de la sombra del texto y la opacidad - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" # empty strings from id 32501 to 32504 msgctxt "#32505" msgid "Choose background image aspect ratio:" -msgstr "" +msgstr "Elegir la relación de aspecto de la imagen de fondo:" msgctxt "#32506" msgid "Fill the screen" -msgstr "" +msgstr "Llenar la pantalla" msgctxt "#32507" msgid "Original size" -msgstr "" +msgstr "Tamaño original" msgctxt "#32510" msgid "Choose background" @@ -495,28 +504,28 @@ msgstr "Un color" msgctxt "#32512" msgid "Single image" -msgstr "Una imagen" +msgstr "Imagen única" msgctxt "#32513" msgid "Slideshow" -msgstr "Presentación" +msgstr "Presentación de imágenes" msgctxt "#32514" msgid "Skin Helper Backgrounds" -msgstr "" +msgstr "Fondos para el tema del servicio de ayuda" msgctxt "#32515" msgid "Dim" -msgstr "" +msgstr "Oscuro" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "Nivel de brillo" # empty strings from id 32517 to 32519 msgctxt "#32520" msgid "Choose background color - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" -msgstr "" +msgstr "Eligir el color de fondo - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" msgctxt "#32530" msgid "Choose image" @@ -528,7 +537,7 @@ msgstr "¡Atención! ¡Tener una imagen fija en la pantalla puede quemarla!" msgctxt "#32540" msgid "Choose slideshow directory" -msgstr "Elegir carpeta de presentación" +msgstr "Elegir el directorio de la presentación de imágenes" msgctxt "#32550" msgid "Random images" @@ -536,7 +545,7 @@ msgstr "Imágenes aleatorias" msgctxt "#32560" msgid "Change background picture every:" -msgstr "Cambiar imágen de fondo cada:" +msgstr "Cambiar imagen de fondo cada:" msgctxt "#32561" msgid "15 sec" @@ -589,52 +598,64 @@ msgstr "10 min" # empty strings from id 32573 to 32578 msgctxt "#32579" msgid "Requires skin.helper.backgrounds!" -msgstr "" +msgstr "¡Requiere skin.helper.backgrounds!" msgctxt "#32580" msgid "Choose Skin Helper background" -msgstr "" +msgstr "Eligir el fondo del tema del servicio de ayuda" msgctxt "#32581" msgid "Movie random fanart" -msgstr "" +msgstr "Fanart aleatorio de películas" msgctxt "#32582" msgid "TV show random fanart" -msgstr "" +msgstr "Fanart aleatorio de programas de TV" msgctxt "#32583" msgid "Music artist random fanart" -msgstr "" +msgstr "Fanart aleatorio de artista musical" msgctxt "#32584" msgid "Random fanart of all media types" -msgstr "" +msgstr "Fanart aleatorio de todo tipo de medios" # empty strings from id 32585 to 32599 msgctxt "#32600" msgid "Element size increase (%)" -msgstr "" +msgstr "Aumento del tamaño del elemento (%)" msgctxt "#32610" msgid "Log out" -msgstr "" +msgstr "Cerrar sesión" msgctxt "#32611" msgid "Stop now playing media" -msgstr "" +msgstr "Detener ahora la reproducción multimedia" msgctxt "#32612" msgid "Log out after (minutes)" -msgstr "" +msgstr "Cerrar sesión después de (minutos)" msgctxt "#32620" msgid "Enable RSS" -msgstr "" +msgstr "Habilitar RSS" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" -msgstr "" +msgstr "¡RSS debe estar correctamente configurado en Kodi y activado!" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "Apagar la pantalla a través de CEC" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "Detener ahora la reproducción multimedia" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "Apagar la pantalla a través de CEC después de (minutos)" #~ msgctxt "#32010" #~ msgid "Colors" diff --git a/screensaver.digitalclock/resources/language/resource.language.es_mx/strings.po b/screensaver.digitalclock/resources/language/resource.language.es_mx/strings.po index 53d82758f..dbb40e228 100644 --- a/screensaver.digitalclock/resources/language/resource.language.es_mx/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.es_mx/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Spanish (Mexico) \n" "Language: es_mx\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -50,7 +51,7 @@ msgstr "" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "Segundo plano" # empty strings from id 32021 to 32024 msgctxt "#32025" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lento" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rápido" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.et_ee/strings.po b/screensaver.digitalclock/resources/language/resource.language.et_ee/strings.po index c30146bf7..cb910b6ad 100644 --- a/screensaver.digitalclock/resources/language/resource.language.et_ee/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.et_ee/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-10 15:00+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Estonian \n" "Language: et_ee\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Aeglane" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normaalne" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Kiire" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.eu_es/strings.po b/screensaver.digitalclock/resources/language/resource.language.eu_es/strings.po index 9fba71740..427b36f11 100644 --- a/screensaver.digitalclock/resources/language/resource.language.eu_es/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.eu_es/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Basque (Spain) \n" "Language: eu_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Poliki" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Arrunta" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Azkarra" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.fa_af/strings.po b/screensaver.digitalclock/resources/language/resource.language.fa_af/strings.po index 4c0d09ba8..24ebfde47 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fa_af/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fa_af/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-09 13:42+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Persian (Afghanistan) \n" "Language: fa_af\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "نورمال" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.fa_ir/strings.po b/screensaver.digitalclock/resources/language/resource.language.fa_ir/strings.po index bfd40951d..db1445e13 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fa_ir/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fa_ir/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Persian (Iran) \n" "Language: fa_ir\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "نرمال" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.fi_fi/strings.po b/screensaver.digitalclock/resources/language/resource.language.fi_fi/strings.po index dcf7533e5..4edb420e8 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fi_fi/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fi_fi/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-08-20 09:14+0000\n" +"Last-Translator: Oskari Lavinto \n" +"Language-Team: Finnish \n" "Language: fi_fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.13\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -50,7 +51,7 @@ msgstr "" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "Tausta" # empty strings from id 32021 to 32024 msgctxt "#32025" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Hidas" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normaali" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Nopea" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -206,7 +216,7 @@ msgstr "" # empty strings from id 32211 to 32219 msgctxt "#32220" msgid "Enable now playing information" -msgstr "" +msgstr "Näytä tietoja nykyisestä toistosta" msgctxt "#32221" msgid "Show only music information" @@ -509,7 +519,7 @@ msgstr "" msgctxt "#32512" msgid "Single image" -msgstr "" +msgstr "Yksittäinen kuva" msgctxt "#32513" msgid "Slideshow" @@ -525,7 +535,7 @@ msgstr "" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "Kirkkauden taso" # empty strings from id 32517 to 32519 msgctxt "#32520" @@ -640,7 +650,7 @@ msgstr "" msgctxt "#32611" msgid "Stop now playing media" -msgstr "" +msgstr "Pysäytä parhaillaan toistettava media" msgctxt "#32612" msgid "Log out after (minutes)" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.fo_fo/strings.po b/screensaver.digitalclock/resources/language/resource.language.fo_fo/strings.po index a9a831444..0ed1e6593 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fo_fo/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fo_fo/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-10 15:00+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Faroese \n" "Language: fo_fo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,11 +90,11 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Vanligt" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Skjótt" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.fr_ca/strings.po b/screensaver.digitalclock/resources/language/resource.language.fr_ca/strings.po index a1160601c..20fd23cf5 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fr_ca/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fr_ca/strings.po @@ -185,6 +185,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Activer les informations supplémentaires" @@ -635,6 +644,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Couleurs" diff --git a/screensaver.digitalclock/resources/language/resource.language.fr_fr/strings.po b/screensaver.digitalclock/resources/language/resource.language.fr_fr/strings.po index dd1874cb1..3b344eff3 100644 --- a/screensaver.digitalclock/resources/language/resource.language.fr_fr/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.fr_fr/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: French (France) (http://www.transifex.com/projects/p/kodi-addons/language/fr_FR/)\n" -"Language: fr_FR\n" +"PO-Revision-Date: 2023-02-25 18:17+0000\n" +"Last-Translator: skypichat \n" +"Language-Team: French (France) \n" +"Language: fr_fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -185,6 +186,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Activer les infos supplémentaires" @@ -635,6 +645,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Couleurs" diff --git a/screensaver.digitalclock/resources/language/resource.language.gl_es/strings.po b/screensaver.digitalclock/resources/language/resource.language.gl_es/strings.po index 0a22e11e1..3fbc7a9b7 100644 --- a/screensaver.digitalclock/resources/language/resource.language.gl_es/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.gl_es/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Galician (Spain) \n" "Language: gl_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lenta" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rápida" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.he_il/strings.po b/screensaver.digitalclock/resources/language/resource.language.he_il/strings.po index 9224fd58d..cb490bd8d 100644 --- a/screensaver.digitalclock/resources/language/resource.language.he_il/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.he_il/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Hebrew (Israel) \n" "Language: he_il\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && n % 10 == 0) ? 2 : 3));\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -50,7 +51,7 @@ msgstr "" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "רקע" # empty strings from id 32021 to 32024 msgctxt "#32025" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "איטית" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "רגיל" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "מהירה" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.hi_in/strings.po b/screensaver.digitalclock/resources/language/resource.language.hi_in/strings.po index 1fc658676..316724aec 100644 --- a/screensaver.digitalclock/resources/language/resource.language.hi_in/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.hi_in/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-09 13:42+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Hindi (India) \n" "Language: hi_in\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "सामान्य" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.hr_hr/strings.po b/screensaver.digitalclock/resources/language/resource.language.hr_hr/strings.po index 3e57d5a61..e72dae131 100644 --- a/screensaver.digitalclock/resources/language/resource.language.hr_hr/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.hr_hr/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-21 04:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Croatian \n" "Language: hr_hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -50,7 +51,7 @@ msgstr "" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "Pozadina" # empty strings from id 32021 to 32024 msgctxt "#32025" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Sporo" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normalna" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Brzo" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.hu_hu/strings.po b/screensaver.digitalclock/resources/language/resource.language.hu_hu/strings.po index 1a1589fec..a35fc9873 100644 --- a/screensaver.digitalclock/resources/language/resource.language.hu_hu/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.hu_hu/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Hungarian \n" "Language: hu_hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lassú" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normál" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Gyors" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.id_id/strings.po b/screensaver.digitalclock/resources/language/resource.language.id_id/strings.po index 61143ae69..6570cba6f 100644 --- a/screensaver.digitalclock/resources/language/resource.language.id_id/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.id_id/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Indonesian \n" "Language: id_id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Pelan" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Cepat" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.is_is/strings.po b/screensaver.digitalclock/resources/language/resource.language.is_is/strings.po index 5fa801ce9..6c0c70939 100644 --- a/screensaver.digitalclock/resources/language/resource.language.is_is/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.is_is/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Icelandic \n" "Language: is_is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Hægt" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Venjulegt" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Hratt" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.it_it/strings.po b/screensaver.digitalclock/resources/language/resource.language.it_it/strings.po index 457461040..f260bf6b5 100644 --- a/screensaver.digitalclock/resources/language/resource.language.it_it/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.it_it/strings.po @@ -5,37 +5,38 @@ msgid "" msgstr "" "Project-Id-Version: KODI Addons\n" -"Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Italian (Italy) (http://www.transifex.com/projects/p/kodi-addons/language/it_IT/)\n" -"Language: it_IT\n" +"PO-Revision-Date: 2024-01-12 00:13+0000\n" +"Last-Translator: Massimo Pissarello \n" +"Language-Team: Italian \n" +"Language: it_it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.3\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "Salvaschermo orologio digitale" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "Screensaver orologio digitale con data, informazioni su elementi in riproduzione, meteo, presentazione immagini e altre opzioni." +msgstr "Salvaschermo orologio digitale con data, informazioni su elementi in riproduzione, meteo, presentazione immagini e altre opzioni." msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "Per bug, richieste o domande generali visita il thread dello Screensaver orologio digitale sul forum Kodi." +msgstr "Per bug, richieste o domande generali visita il thread del salvaschermo orologio digitale sul forum Kodi." # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "Movimento" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "Formato" msgctxt "#32005" msgid "Additional Information" @@ -44,16 +45,16 @@ msgstr "Informazioni aggiuntive" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "Colore | Opacità" msgctxt "#32020" msgid "Background" -msgstr "Sfond" +msgstr "Sfondo" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "Opzioni extra" msgctxt "#32100" msgid "Movement on the screen" @@ -69,7 +70,7 @@ msgstr "Rimbalzo" msgctxt "#32103" msgid "Fixed in the center" -msgstr "Fisso al centro" +msgstr "Fissato al centro" msgctxt "#32104" msgid "Custom fixed position" @@ -93,7 +94,7 @@ msgstr "Veloce" msgctxt "#32120" msgid "Number of seconds to stay in place" -msgstr "Numero di secondi in posizione" +msgstr "Secondi per rimanere sul posto" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" @@ -101,7 +102,7 @@ msgstr "Attenzione! Un testo statico sullo schermo può causare il burn-in!" msgctxt "#32130" msgid "Time format" -msgstr "Formato ora" +msgstr "Formato orario" msgctxt "#32131" msgid "17:14" @@ -117,24 +118,24 @@ msgstr "05:14 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" -msgstr "" +msgstr "5:14 Nessun riempimento ora zero Windows" msgctxt "#32135" msgid "5:14 PM No hour zero padding Windows" -msgstr "" +msgstr "5:14 PM Nessun riempimento ora zero Windows" msgctxt "#32136" msgid "5:14 No hour zero padding Unix" -msgstr "" +msgstr "5:14 Nessun riempimento ora zero Unix" msgctxt "#32137" msgid "5:14 PM No hour zero padding Unix" -msgstr "" +msgstr "5:14 PM Nessun riempimento ora zero Unix" # empty strings from id 32138 to 32138 msgctxt "#32139" msgid "Colon blink" -msgstr "" +msgstr "Lampeggio due punti" msgctxt "#32140" msgid "Date format" @@ -158,7 +159,7 @@ msgstr "05.30.2015" msgctxt "#32145" msgid "Custom" -msgstr "" +msgstr "Personalizzata" msgctxt "#32150" msgid "Custom value for X position (1-1280)" @@ -171,19 +172,28 @@ msgstr "Valore personalizzato asse Y (1-720)" # empty strings from id 32161 to 32169 msgctxt "#32170" msgid "Enter custom date format (Empty=Regional settings):" -msgstr "" +msgstr "Inserisci formato data personalizzato (Vuoto=Impostazioni regionali):" msgctxt "#32171" msgid "Day: d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" -msgstr "" +msgstr "Giorno: d(1-31) dd(01-31) ddd(lun-dom) DDD(lunedì-domenica)" msgctxt "#32172" msgid "Month: m(1-12) mm(01-12) mmm(Jan-Dec) MMM(January-December)" -msgstr "" +msgstr "Mese: m(1-12) mm(01-12) mmm(gen-dic) MMM(gennaio-dicembre)" msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" -msgstr "" +msgstr "Anno: yy (anno a 2 cifre) yyyy (anno a 4 cifre)" + +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "17:14 Nessun riempimento zero per le ore su Windows" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "17:14 Nessun riempimento zero per le ore su Unix" msgctxt "#32200" msgid "Enable additional information" @@ -195,19 +205,19 @@ msgstr "Numero di secondi per cambio informazione" msgctxt "#32220" msgid "Enable now playing information" -msgstr "Abilita infomazioni su elementi in riproduzione" +msgstr "Abilita ora informazioni di riproduzione" msgctxt "#32221" msgid "Show only music information" -msgstr "" +msgstr "Mostra solo informazioni sulla musica" msgctxt "#32222" msgid "This will hide all other info while the music is playing!" -msgstr "" +msgstr "Nasconde tutte le altre informazioni mentre la musica è in riproduzione!" msgctxt "#32223" msgid "Merge song artist and title" -msgstr "" +msgstr "Unisci artista e titolo della canzone" msgctxt "#32230" msgid "Enable weather information" @@ -244,245 +254,245 @@ msgstr "Abilita immagine album" # empty strings from id 32251 to 32259 msgctxt "#32260" msgid "Enable CPU usage information" -msgstr "" +msgstr "Abilita informazioni utilizzo CPU" msgctxt "#32261" msgid "Enable battery level information" -msgstr "" +msgstr "Abilita informazioni livello batteria" msgctxt "#32262" msgid "Enable free memory information" -msgstr "" +msgstr "Abilita informazioni memoria libera" msgctxt "#32263" msgid "Enable movie library information" -msgstr "" +msgstr "Abilita informazioni libreria film" msgctxt "#32264" msgid "Enable TV show library information" -msgstr "" +msgstr "Abilita informazioni libreria programmi TV" msgctxt "#32265" msgid "Enable music library information" -msgstr "" +msgstr "Abilita informazioni libreria musicale" msgctxt "#32266" msgid "Enable CPU temperature" -msgstr "" +msgstr "Abilita temperatura CPU" msgctxt "#32267" msgid "Enable GPU temperature" -msgstr "" +msgstr "Abilita temperatura GPU" msgctxt "#32268" msgid "Enable HDD temperature" -msgstr "" +msgstr "Abilita temperatura HDD" msgctxt "#32269" msgid "Enable FPS" -msgstr "" +msgstr "Abilita FPS" msgctxt "#32270" msgid "Enable current uptime" -msgstr "" +msgstr "Abilita tempo attività corrente" msgctxt "#32271" msgid "Enable total uptime" -msgstr "" +msgstr "Abilita tempo attività totale" # empty strings from id 32272 to 32279 msgctxt "#32280" msgid "CPU:" -msgstr "" +msgstr "CPU:" msgctxt "#32281" msgid "Battery:" -msgstr "" +msgstr "Batteria:" msgctxt "#32282" msgid "Free memory:" -msgstr "" +msgstr "Memoria libera:" msgctxt "#32283" msgid "Total movies:" -msgstr "" +msgstr "Totale film:" msgctxt "#32284" msgid "Watched movies:" -msgstr "" +msgstr "Film visti:" msgctxt "#32285" msgid "Unwatched movies:" -msgstr "" +msgstr "Film non visti:" msgctxt "#32286" msgid "Total TV shows:" -msgstr "" +msgstr "Totale programmi TV:" msgctxt "#32287" msgid "Total episodes:" -msgstr "" +msgstr "Totale episodi:" msgctxt "#32288" msgid "Watched TV shows:" -msgstr "" +msgstr "Programmi TV visti:" msgctxt "#32289" msgid "Watched episodes:" -msgstr "" +msgstr "Episodi visti:" msgctxt "#32290" msgid "Unwatched TV shows:" -msgstr "" +msgstr "Programmi TV non visti:" msgctxt "#32291" msgid "Unwatched episodes:" -msgstr "" +msgstr "Episodi non visti:" msgctxt "#32292" msgid "Artists:" -msgstr "" +msgstr "Artisti:" msgctxt "#32293" msgid "Albums:" -msgstr "" +msgstr "Album:" msgctxt "#32294" msgid "Songs:" -msgstr "" +msgstr "Canzoni:" msgctxt "#32295" msgid "CPU temperature:" -msgstr "" +msgstr "Temperatura CPU:" msgctxt "#32296" msgid "GPU temperature:" -msgstr "" +msgstr "Temperatura GPU:" msgctxt "#32297" msgid "HDD temperature:" -msgstr "" +msgstr "Temperatura HDD:" msgctxt "#32298" msgid "FPS:" -msgstr "" +msgstr "FPS:" msgctxt "#32299" msgid "Current uptime:" -msgstr "" +msgstr "Tempo attività attuale:" msgctxt "#32300" msgid "Total uptime:" -msgstr "" +msgstr "Tempo attività totale:" # empty strings from id 32301 to 32309 msgctxt "#32310" msgid "Choose hour color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità dell'ora - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" msgctxt "#32311" msgid "Random hour color" -msgstr "" +msgstr "Colore dell'ora casuale" msgctxt "#32312" msgid "Random hour opacity" -msgstr "" +msgstr "Opacità dell'ora casuale" # empty strings from id 32313 to 32319 msgctxt "#32320" msgid "Choose colon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità dei due punti - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" msgctxt "#32321" msgid "Random colon color" -msgstr "" +msgstr "Colore dei due punti casuale" msgctxt "#32322" msgid "Random colon opacity" -msgstr "" +msgstr "Opacità dei due punti casuale" # empty strings from id 32323 to 32329 msgctxt "#32330" msgid "Choose minute color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità del minuto - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" msgctxt "#32331" msgid "Random minute color" -msgstr "" +msgstr "Colore del minuto casuale" msgctxt "#32332" msgid "Random minute opacity" -msgstr "" +msgstr "Opacità del minuto casuale" # empty strings from id 32333 to 32339 msgctxt "#32340" msgid "Choose AM/PM color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità AM/PM - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" msgctxt "#32341" msgid "Random AM/PM color" -msgstr "" +msgstr "Colore di AM/PM casuale" msgctxt "#32342" msgid "Random AM/PM opacity" -msgstr "" +msgstr "Opacità di AM/PM casuale" # empty strings from id 32343 to 32349 msgctxt "#32350" msgid "Choose date color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità della data - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" msgctxt "#32351" msgid "Random date color" -msgstr "" +msgstr "Colore della data casuale" msgctxt "#32352" msgid "Random date opacity" -msgstr "" +msgstr "Opacità della data casuale" # empty strings from id 32353 to 32359 msgctxt "#32360" msgid "Choose additional information color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli informazioni aggiuntive su colore e opacità - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" msgctxt "#32361" msgid "Random additional information color" -msgstr "" +msgstr "Colore delle informazioni aggiuntive casuali" msgctxt "#32362" msgid "Random additional information opacity" -msgstr "" +msgstr "Opacità delle informazioni aggiuntive casuale" # empty strings from id 32363 to 32369 msgctxt "#32370" msgid "Choose icon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore e l'opacità dell'icona - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" msgctxt "#32371" msgid "Random icon color" -msgstr "" +msgstr "Colore dell'icona casuale" msgctxt "#32372" msgid "Random icon opacity" -msgstr "" +msgstr "Opacità dell'icona casuale" # empty strings from id 32373 to 32499 msgctxt "#32500" msgid "Choose text shadow color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore dell'ombreggiatura e l'opacità del testo - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" # empty strings from id 32501 to 32504 msgctxt "#32505" msgid "Choose background image aspect ratio:" -msgstr "" +msgstr "Scegli le proporzioni dell'immagine di sfondo:" msgctxt "#32506" msgid "Fill the screen" -msgstr "" +msgstr "Riempi lo schermo" msgctxt "#32507" msgid "Original size" -msgstr "" +msgstr "Dimensione originale" msgctxt "#32510" msgid "Choose background" @@ -490,7 +500,7 @@ msgstr "Seleziona sfondo" msgctxt "#32511" msgid "One color" -msgstr "Colore" +msgstr "Un colore" msgctxt "#32512" msgid "Single image" @@ -502,20 +512,20 @@ msgstr "Presentazione" msgctxt "#32514" msgid "Skin Helper Backgrounds" -msgstr "" +msgstr "Sfondi per l'aiuto della skin" msgctxt "#32515" msgid "Dim" -msgstr "" +msgstr "Oscura" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "Livello luminosità" # empty strings from id 32517 to 32519 msgctxt "#32520" msgid "Choose background color - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" -msgstr "" +msgstr "Scegli il colore di sfondo - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" msgctxt "#32530" msgid "Choose image" @@ -588,52 +598,64 @@ msgstr "10 min" # empty strings from id 32573 to 32578 msgctxt "#32579" msgid "Requires skin.helper.backgrounds!" -msgstr "" +msgstr "Richiede skin.helper.backgrounds!" msgctxt "#32580" msgid "Choose Skin Helper background" -msgstr "" +msgstr "Scegli lo sfondo di Skin Helper" msgctxt "#32581" msgid "Movie random fanart" -msgstr "" +msgstr "Fanart casuale film" msgctxt "#32582" msgid "TV show random fanart" -msgstr "" +msgstr "Fanart casuale programma TV" msgctxt "#32583" msgid "Music artist random fanart" -msgstr "" +msgstr "Fanart casuale artista musicale" msgctxt "#32584" msgid "Random fanart of all media types" -msgstr "" +msgstr "Fanart casuale tutti i file multimediali" # empty strings from id 32585 to 32599 msgctxt "#32600" msgid "Element size increase (%)" -msgstr "" +msgstr "Aumento dimensione elemento (%)" msgctxt "#32610" msgid "Log out" -msgstr "" +msgstr "Disconnetti" msgctxt "#32611" msgid "Stop now playing media" -msgstr "" +msgstr "Interrompi ora la riproduzione" msgctxt "#32612" msgid "Log out after (minutes)" -msgstr "" +msgstr "Disconnetti dopo (minuti)" msgctxt "#32620" msgid "Enable RSS" -msgstr "" +msgstr "Abilita RSS" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" -msgstr "" +msgstr "RSS deve essere configurato e abilitato correttamente in Kodi!" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "Spegni schermo tramite CEC" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "Interrompi ora la riproduzione" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "Spegni schermo tramite CEC dopo (minuti)" #~ msgctxt "#32010" #~ msgid "Colors" diff --git a/screensaver.digitalclock/resources/language/resource.language.ja_jp/strings.po b/screensaver.digitalclock/resources/language/resource.language.ja_jp/strings.po index 58126845b..4f84fe3a0 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ja_jp/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ja_jp/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Japanese \n" "Language: ja_jp\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "ゆっくり" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "ノーマル" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "速く" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ko_kr/strings.po b/screensaver.digitalclock/resources/language/resource.language.ko_kr/strings.po index 7dcfd7393..cc6fb45a4 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ko_kr/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ko_kr/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Minho Park \n" +"Language-Team: Korean \n" "Language: ko_kr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "느림" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "보통" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "빠름" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.lt_lt/strings.po b/screensaver.digitalclock/resources/language/resource.language.lt_lt/strings.po index e309f46ce..11dbaa327 100644 --- a/screensaver.digitalclock/resources/language/resource.language.lt_lt/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.lt_lt/strings.po @@ -185,6 +185,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Įjungti papildomą informaciją" @@ -635,6 +644,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Spalvos" diff --git a/screensaver.digitalclock/resources/language/resource.language.lv_lv/strings.po b/screensaver.digitalclock/resources/language/resource.language.lv_lv/strings.po index 2598327ad..7bbbb5a6a 100644 --- a/screensaver.digitalclock/resources/language/resource.language.lv_lv/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.lv_lv/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Latvian \n" "Language: lv_lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lēns" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normāls" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Ātrs" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.mi/strings.po b/screensaver.digitalclock/resources/language/resource.language.mi/strings.po index 95a9e4ee9..4f17976f8 100644 --- a/screensaver.digitalclock/resources/language/resource.language.mi/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.mi/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-09 13:42+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Maori \n" "Language: mi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,7 +90,7 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Pūnoa" msgctxt "#32113" msgid "Fast" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.mk_mk/strings.po b/screensaver.digitalclock/resources/language/resource.language.mk_mk/strings.po index 0ab8ec059..604f9ef49 100644 --- a/screensaver.digitalclock/resources/language/resource.language.mk_mk/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.mk_mk/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Macedonian \n" "Language: mk_mk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n==1 || n%10==1 ? 0 : 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Споро" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Нормален" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Брзо" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ms_my/strings.po b/screensaver.digitalclock/resources/language/resource.language.ms_my/strings.po index 3d97e2eff..61ab3d6c4 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ms_my/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ms_my/strings.po @@ -185,6 +185,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Benarkan maklumat tambahan" @@ -635,6 +644,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Warna" diff --git a/screensaver.digitalclock/resources/language/resource.language.mt_mt/strings.po b/screensaver.digitalclock/resources/language/resource.language.mt_mt/strings.po index 4941dc067..1e6080582 100644 --- a/screensaver.digitalclock/resources/language/resource.language.mt_mt/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.mt_mt/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Maltese \n" "Language: mt_mt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -89,11 +90,11 @@ msgstr "" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normali" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Mgħaġġel" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.my_mm/strings.po b/screensaver.digitalclock/resources/language/resource.language.my_mm/strings.po index 9fb67f744..018c3138b 100644 --- a/screensaver.digitalclock/resources/language/resource.language.my_mm/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.my_mm/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Burmese \n" "Language: my_mm\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "နှေးသော" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "ပုံမှန်" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "အမြန်" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.nb_no/strings.po b/screensaver.digitalclock/resources/language/resource.language.nb_no/strings.po index 0e744304c..571a22d7e 100644 --- a/screensaver.digitalclock/resources/language/resource.language.nb_no/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.nb_no/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-27 01:16+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Norwegian Bokmål \n" "Language: nb_no\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Sakte" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rask" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.nl_nl/strings.po b/screensaver.digitalclock/resources/language/resource.language.nl_nl/strings.po index 74d1f4762..177e4ede9 100644 --- a/screensaver.digitalclock/resources/language/resource.language.nl_nl/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.nl_nl/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-10 15:00+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Dutch \n" "Language: nl_nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Traag" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normaal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Snel" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.pl_pl/strings.po b/screensaver.digitalclock/resources/language/resource.language.pl_pl/strings.po index 4f09bb0cd..1db1d25cc 100644 --- a/screensaver.digitalclock/resources/language/resource.language.pl_pl/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.pl_pl/strings.po @@ -5,17 +5,17 @@ msgid "" msgstr "" "Project-Id-Version: KODI Addons\n" -"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2021-09-06 18:29+0000\n" -"Last-Translator: Marek Adamski \n" +"PO-Revision-Date: 2022-07-01 11:14+0000\n" +"Last-Translator: Christian Gade \n" "Language-Team: Polish \n" "Language: pl_pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.8\n" +"X-Generator: Weblate 4.13\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -186,6 +186,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Aktywuj dodatkowe informacje" @@ -636,6 +645,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Kolory" diff --git a/screensaver.digitalclock/resources/language/resource.language.pt_br/strings.po b/screensaver.digitalclock/resources/language/resource.language.pt_br/strings.po index 3658abba3..d76764c38 100644 --- a/screensaver.digitalclock/resources/language/resource.language.pt_br/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.pt_br/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-30 09:23+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Portuguese (Brazil) \n" "Language: pt_br\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Devagar" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rápido" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.pt_pt/strings.po b/screensaver.digitalclock/resources/language/resource.language.pt_pt/strings.po index 4d78c2d92..876b6f974 100644 --- a/screensaver.digitalclock/resources/language/resource.language.pt_pt/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.pt_pt/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Portuguese (Portugal) \n" "Language: pt_pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Lento" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rápido" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ro_ro/strings.po b/screensaver.digitalclock/resources/language/resource.language.ro_ro/strings.po index e45889479..2204e4d56 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ro_ro/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ro_ro/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Romanian \n" "Language: ro_ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Încet" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rapidă" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ru_ru/strings.po b/screensaver.digitalclock/resources/language/resource.language.ru_ru/strings.po index 10ec99997..e44ad6bc4 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ru_ru/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ru_ru/strings.po @@ -7,35 +7,36 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/kodi-addons/language/ru_RU/)\n" -"Language: ru_RU\n" +"PO-Revision-Date: 2022-09-13 11:14+0000\n" +"Last-Translator: Andrei Stepanov \n" +"Language-Team: Russian \n" +"Language: ru_ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.14\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "Застава \"Цифровые часы\"" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "" +msgstr "Заставка цифровых часов, с датой, с информацией о погоде, со слайд-шоу и с несколькими опциями." msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "" +msgstr "Для сообщений об ошибках, для пожеланий, для общих вопросов, посетите тему дополнения в форуме Kodi." # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "Движение" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "Формат" msgctxt "#32005" msgid "Additional Information" @@ -44,7 +45,7 @@ msgstr "Дополнительная информация" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "Цвет | Непрозрачность" msgctxt "#32020" msgid "Background" @@ -53,71 +54,71 @@ msgstr "Задний фон" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "Дополнительные опции" # empty strings from id 32026 to 32099 msgctxt "#32100" msgid "Movement on the screen" -msgstr "" +msgstr "Движение на экране" msgctxt "#32101" msgid "Random position" -msgstr "" +msgstr "Случайное положение" msgctxt "#32102" msgid "Bounce" -msgstr "" +msgstr "Отскок" msgctxt "#32103" msgid "Fixed in the center" -msgstr "" +msgstr "Зафиксировать в центре" msgctxt "#32104" msgid "Custom fixed position" -msgstr "" +msgstr "Зафиксировать на позиции" # empty strings from id 32105 to 32109 msgctxt "#32110" msgid "Movement speed" -msgstr "" +msgstr "Скорость движения" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Медленно" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Нормальное" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Быстро" # empty strings from id 32114 to 32119 msgctxt "#32120" msgid "Number of seconds to stay in place" -msgstr "" +msgstr "Число секунд на ожидание на месте" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" -msgstr "" +msgstr "Предупреждение! Наличие статического текста на экране может привести к выгоранию!" # empty strings from id 32122 to 32129 msgctxt "#32130" msgid "Time format" -msgstr "" +msgstr "Формат времени" msgctxt "#32131" msgid "17:14" -msgstr "" +msgstr "17:14" msgctxt "#32132" msgid "05:14" -msgstr "" +msgstr "05:14" msgctxt "#32133" msgid "05:14 PM" -msgstr "" +msgstr "05:14 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" @@ -138,46 +139,46 @@ msgstr "" # empty strings from id 32138 to 32138 msgctxt "#32139" msgid "Colon blink" -msgstr "" +msgstr "Мигающее двоеточие" msgctxt "#32140" msgid "Date format" -msgstr "" +msgstr "Формат даты" msgctxt "#32141" msgid "Hide date" -msgstr "" +msgstr "Не отображать дату" msgctxt "#32142" msgid "Saturday 30. May 2015" -msgstr "" +msgstr "Суббота 30. Май 2015" msgctxt "#32143" msgid "30.05.2015" -msgstr "" +msgstr "30.05.2015" msgctxt "#32144" msgid "05.30.2015" -msgstr "" +msgstr "05.30.2015" msgctxt "#32145" msgid "Custom" -msgstr "" +msgstr "Заданный" # empty strings from id 32146 to 32149 msgctxt "#32150" msgid "Custom value for X position (1-1280)" -msgstr "" +msgstr "Позиция X (1-1280)" # empty strings from id 32151 to 32159 msgctxt "#32160" msgid "Custom value for Y position (1-720)" -msgstr "" +msgstr "Позиция Y (1-720)" # empty strings from id 32161 to 32169 msgctxt "#32170" msgid "Enter custom date format (Empty=Regional settings):" -msgstr "" +msgstr "Введите формат даты (Пустой = настройки региона):" msgctxt "#32171" msgid "Day: d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" @@ -191,24 +192,33 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" -msgstr "" +msgstr "Включить дополнительную информацию" # empty strings from id 32201 to 32209 msgctxt "#32210" msgid "Number of seconds for information switch" -msgstr "" +msgstr "Время переключения информации в секундах" # empty strings from id 32211 to 32219 msgctxt "#32220" msgid "Enable now playing information" -msgstr "" +msgstr "Включить информацию о проигрываемом сейчас" msgctxt "#32221" msgid "Show only music information" -msgstr "" +msgstr "Показывать только сведения о музыке" msgctxt "#32222" msgid "This will hide all other info while the music is playing!" @@ -221,32 +231,32 @@ msgstr "" # empty strings from id 32224 to 32229 msgctxt "#32230" msgid "Enable weather information" -msgstr "" +msgstr "Включить информацию о погоде" # empty strings from id 32231 to 32239 msgctxt "#32240" msgid "Weather icons" -msgstr "" +msgstr "Значки погоды" msgctxt "#32241" msgid "Hide weather icon" -msgstr "" +msgstr "Скрыть значки погоды" msgctxt "#32242" msgid "Icon set 1" -msgstr "" +msgstr "Набор значков 1" msgctxt "#32243" msgid "Icon set 2" -msgstr "" +msgstr "Набор значков 2" msgctxt "#32244" msgid "Icon set 3" -msgstr "" +msgstr "Набор значков 3" msgctxt "#32245" msgid "Icon set 4" -msgstr "" +msgstr "Набор значков 4" # empty strings from id 32246 to 32249 msgctxt "#32250" @@ -256,15 +266,15 @@ msgstr "" # empty strings from id 32251 to 32259 msgctxt "#32260" msgid "Enable CPU usage information" -msgstr "" +msgstr "Включить информацию о использовании ЦП" msgctxt "#32261" msgid "Enable battery level information" -msgstr "" +msgstr "Включить информацию о заряде батареи" msgctxt "#32262" msgid "Enable free memory information" -msgstr "" +msgstr "Включить информацию о свободной памяти" msgctxt "#32263" msgid "Enable movie library information" @@ -280,40 +290,40 @@ msgstr "" msgctxt "#32266" msgid "Enable CPU temperature" -msgstr "" +msgstr "Включить температуру ЦП" msgctxt "#32267" msgid "Enable GPU temperature" -msgstr "" +msgstr "Включить температуру ГП" msgctxt "#32268" msgid "Enable HDD temperature" -msgstr "" +msgstr "Включить температуру HDD" msgctxt "#32269" msgid "Enable FPS" -msgstr "" +msgstr "Включить частоту кадров" msgctxt "#32270" msgid "Enable current uptime" -msgstr "" +msgstr "Включить текущее время работы" msgctxt "#32271" msgid "Enable total uptime" -msgstr "" +msgstr "Включить общее время работы" # empty strings from id 32272 to 32279 msgctxt "#32280" msgid "CPU:" -msgstr "" +msgstr "ЦП:" msgctxt "#32281" msgid "Battery:" -msgstr "" +msgstr "Батарея:" msgctxt "#32282" msgid "Free memory:" -msgstr "" +msgstr "Свободно памяти:" msgctxt "#32283" msgid "Total movies:" @@ -365,27 +375,27 @@ msgstr "" msgctxt "#32295" msgid "CPU temperature:" -msgstr "" +msgstr "Температура ЦП:" msgctxt "#32296" msgid "GPU temperature:" -msgstr "" +msgstr "Температура ГП:" msgctxt "#32297" msgid "HDD temperature:" -msgstr "" +msgstr "Температура HDD:" msgctxt "#32298" msgid "FPS:" -msgstr "" +msgstr "Частота кадров:" msgctxt "#32299" msgid "Current uptime:" -msgstr "" +msgstr "Текущее время работы:" msgctxt "#32300" msgid "Total uptime:" -msgstr "" +msgstr "Общее время работы:" # empty strings from id 32301 to 32309 msgctxt "#32310" @@ -490,11 +500,11 @@ msgstr "" msgctxt "#32506" msgid "Fill the screen" -msgstr "" +msgstr "Заполнить экран" msgctxt "#32507" msgid "Original size" -msgstr "" +msgstr "Оригинальный размер" # empty strings from id 32508 to 32509 msgctxt "#32510" @@ -503,15 +513,15 @@ msgstr "" msgctxt "#32511" msgid "One color" -msgstr "" +msgstr "Один цвет" msgctxt "#32512" msgid "Single image" -msgstr "" +msgstr "Одно изображение" msgctxt "#32513" msgid "Slideshow" -msgstr "" +msgstr "Слайд-шоу" msgctxt "#32514" msgid "Skin Helper Backgrounds" @@ -519,11 +529,11 @@ msgstr "" msgctxt "#32515" msgid "Dim" -msgstr "" +msgstr "Затемнение" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "Уровень яркости" # empty strings from id 32517 to 32519 msgctxt "#32520" @@ -552,55 +562,55 @@ msgstr "" # empty strings from id 32551 to 32559 msgctxt "#32560" msgid "Change background picture every:" -msgstr "" +msgstr "Менять фон каждые:" msgctxt "#32561" msgid "15 sec" -msgstr "" +msgstr "15 сек" msgctxt "#32562" msgid "30 sec" -msgstr "" +msgstr "30 сек" msgctxt "#32563" msgid "1 min" -msgstr "" +msgstr "1 мин" msgctxt "#32564" msgid "2 min" -msgstr "" +msgstr "2 мин" msgctxt "#32565" msgid "3 min" -msgstr "" +msgstr "3 мин" msgctxt "#32566" msgid "4 min" -msgstr "" +msgstr "4 мин" msgctxt "#32567" msgid "5 min" -msgstr "" +msgstr "5 мин" msgctxt "#32568" msgid "6 min" -msgstr "" +msgstr "6 мин" msgctxt "#32569" msgid "7 min" -msgstr "" +msgstr "7 мин" msgctxt "#32570" msgid "8 min" -msgstr "" +msgstr "8 мин" msgctxt "#32571" msgid "9 min" -msgstr "" +msgstr "9 мин" msgctxt "#32572" msgid "10 min" -msgstr "" +msgstr "10 мин" # empty strings from id 32573 to 32578 msgctxt "#32579" @@ -646,10 +656,22 @@ msgstr "" msgctxt "#32620" msgid "Enable RSS" -msgstr "" +msgstr "Включить RSS" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" +msgstr "RSS должен быть включен и правильно настроен в Kodi!" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" msgstr "" #~ msgctxt "#32010" diff --git a/screensaver.digitalclock/resources/language/resource.language.sk_sk/strings.po b/screensaver.digitalclock/resources/language/resource.language.sk_sk/strings.po index 7813b1550..c6032c679 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sk_sk/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sk_sk/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Slovak \n" "Language: sk_sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Pomalo" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normálny" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Rýchlo" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.sl_si/strings.po b/screensaver.digitalclock/resources/language/resource.language.sl_si/strings.po index 270b9643b..41471a34d 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sl_si/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sl_si/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Slovenian \n" "Language: sl_si\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Počasno" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Običajno" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Hitro" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.sq_al/strings.po b/screensaver.digitalclock/resources/language/resource.language.sq_al/strings.po index cd3e49882..d4368ab65 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sq_al/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sq_al/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Albanian \n" "Language: sq_al\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Ngadal" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Shpejt" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.sr_rs/strings.po b/screensaver.digitalclock/resources/language/resource.language.sr_rs/strings.po index 4530d7382..063f78a26 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sr_rs/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sr_rs/strings.po @@ -186,6 +186,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Омогући додатне податке" @@ -636,6 +645,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Боје" diff --git a/screensaver.digitalclock/resources/language/resource.language.sr_rs@latin/strings.po b/screensaver.digitalclock/resources/language/resource.language.sr_rs@latin/strings.po index 12024d5ee..4b0e039a0 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sr_rs@latin/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sr_rs@latin/strings.po @@ -186,6 +186,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Omogući dodatne podatke" @@ -636,6 +645,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Boje" diff --git a/screensaver.digitalclock/resources/language/resource.language.sv_se/strings.po b/screensaver.digitalclock/resources/language/resource.language.sv_se/strings.po index 771ffb5ad..07fe739f0 100644 --- a/screensaver.digitalclock/resources/language/resource.language.sv_se/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.sv_se/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Swedish \n" "Language: sv_se\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Långsam" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Normal" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Snabb" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.ta_in/strings.po b/screensaver.digitalclock/resources/language/resource.language.ta_in/strings.po index bbc564acd..af50f4447 100644 --- a/screensaver.digitalclock/resources/language/resource.language.ta_in/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.ta_in/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Tamil (India) \n" "Language: ta_in\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "மெதுவாக" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "இயல்பு" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "வேகமாக" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.tg_tj/strings.po b/screensaver.digitalclock/resources/language/resource.language.tg_tj/strings.po index 405fd7347..5d1fa40d1 100644 --- a/screensaver.digitalclock/resources/language/resource.language.tg_tj/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.tg_tj/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Tajik \n" "Language: tg_tj\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Суст" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Оддӣ" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Тез" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.th_th/strings.po b/screensaver.digitalclock/resources/language/resource.language.th_th/strings.po index f0405779e..80e187c3d 100644 --- a/screensaver.digitalclock/resources/language/resource.language.th_th/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.th_th/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Thai \n" "Language: th_th\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "ช้า" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "ปกติ" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "เร็ว" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.tr_tr/strings.po b/screensaver.digitalclock/resources/language/resource.language.tr_tr/strings.po index acd5c0d05..926e83330 100644 --- a/screensaver.digitalclock/resources/language/resource.language.tr_tr/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.tr_tr/strings.po @@ -185,6 +185,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + msgctxt "#32200" msgid "Enable additional information" msgstr "Ek bilgiyi etkinleştir" @@ -635,6 +644,18 @@ msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" + #~ msgctxt "#32010" #~ msgid "Colors" #~ msgstr "Renkler" diff --git a/screensaver.digitalclock/resources/language/resource.language.uk_ua/strings.po b/screensaver.digitalclock/resources/language/resource.language.uk_ua/strings.po index 042d38d5b..bf2abc53b 100644 --- a/screensaver.digitalclock/resources/language/resource.language.uk_ua/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.uk_ua/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Ukrainian \n" "Language: uk_ua\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Повільно" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Нормальне" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Швидкість" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.uz_uz/strings.po b/screensaver.digitalclock/resources/language/resource.language.uz_uz/strings.po index dccf6c097..ce00d054c 100644 --- a/screensaver.digitalclock/resources/language/resource.language.uz_uz/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.uz_uz/strings.po @@ -193,6 +193,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +662,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.vi_vn/strings.po b/screensaver.digitalclock/resources/language/resource.language.vi_vn/strings.po index 8f9c03431..075674e63 100644 --- a/screensaver.digitalclock/resources/language/resource.language.vi_vn/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.vi_vn/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-04-09 10:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Vietnamese \n" "Language: vi_vn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "Chậm" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "Bình thường" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "Nhanh" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.zh_cn/strings.po b/screensaver.digitalclock/resources/language/resource.language.zh_cn/strings.po index ba48a21db..3e4ef808b 100644 --- a/screensaver.digitalclock/resources/language/resource.language.zh_cn/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.zh_cn/strings.po @@ -7,649 +7,671 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2021-11-04 17:55+0000\n" +"Last-Translator: taxigps \n" +"Language-Team: Chinese (China) \n" "Language: zh_cn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.8.1\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" -msgstr "" +msgstr "数字时钟屏幕保护程序" msgctxt "Addon Description" msgid "Digital clock screensaver with date, now playing information, weather information, image slideshow and several options." -msgstr "" +msgstr "数字时钟屏幕保护程序,带有日期、当前播放信息、天气信息、图片幻灯片和多个选项。" msgctxt "Addon Disclaimer" msgid "For bugs, requests or general questions visit the Digital clock screensaver thread on the Kodi forum." -msgstr "" +msgstr "有关 bug、请求或一般问题,请访问 Kodi 论坛上的数字时钟屏幕保护程序讨论贴。" # settings labels msgctxt "#32000" msgid "Movement" -msgstr "" +msgstr "移动" msgctxt "#32001" msgid "Format" -msgstr "" +msgstr "格式" # empty strings from id 32002 to 32004 msgctxt "#32005" msgid "Additional Information" -msgstr "" +msgstr "附加信息" # empty strings from id 32006 to 32009 msgctxt "#32010" msgid "Color | Opacity" -msgstr "" +msgstr "颜色 | 不透明度" # empty strings from id 32011 to 32019 msgctxt "#32020" msgid "Background" -msgstr "" +msgstr "背景" # empty strings from id 32021 to 32024 msgctxt "#32025" msgid "Extra Options" -msgstr "" +msgstr "额外选择" # empty strings from id 32026 to 32099 msgctxt "#32100" msgid "Movement on the screen" -msgstr "" +msgstr "屏幕上的运动" msgctxt "#32101" msgid "Random position" -msgstr "" +msgstr "随机位置" msgctxt "#32102" msgid "Bounce" -msgstr "" +msgstr "反弹" msgctxt "#32103" msgid "Fixed in the center" -msgstr "" +msgstr "固定在中间" msgctxt "#32104" msgid "Custom fixed position" -msgstr "" +msgstr "自定义固定位置" # empty strings from id 32105 to 32109 msgctxt "#32110" msgid "Movement speed" -msgstr "" +msgstr "移动速度" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "慢" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "正常" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "快" # empty strings from id 32114 to 32119 msgctxt "#32120" msgid "Number of seconds to stay in place" -msgstr "" +msgstr "待在原地的秒数" msgctxt "#32121" msgid "Warning! Having static text on your screen can cause a burn in!" -msgstr "" +msgstr "警告!在屏幕上显示静态文本可能会导致烧屏!" # empty strings from id 32122 to 32129 msgctxt "#32130" msgid "Time format" -msgstr "" +msgstr "时间格式" msgctxt "#32131" msgid "17:14" -msgstr "" +msgstr "17:14" msgctxt "#32132" msgid "05:14" -msgstr "" +msgstr "05:14" msgctxt "#32133" msgid "05:14 PM" -msgstr "" +msgstr "05:14 PM" msgctxt "#32134" msgid "5:14 No hour zero padding Windows" -msgstr "" +msgstr "5:14 无小时零填充 Windows" msgctxt "#32135" msgid "5:14 PM No hour zero padding Windows" -msgstr "" +msgstr "5:14 PM 无小时零填充 Windows" msgctxt "#32136" msgid "5:14 No hour zero padding Unix" -msgstr "" +msgstr "5:14 无小时零填充 Unix" msgctxt "#32137" msgid "5:14 PM No hour zero padding Unix" -msgstr "" +msgstr "5:14 PM 无小时零填充 Unix" # empty strings from id 32138 to 32138 msgctxt "#32139" msgid "Colon blink" -msgstr "" +msgstr "冒号闪烁" msgctxt "#32140" msgid "Date format" -msgstr "" +msgstr "日期格式" msgctxt "#32141" msgid "Hide date" -msgstr "" +msgstr "隐藏日期" msgctxt "#32142" msgid "Saturday 30. May 2015" -msgstr "" +msgstr "Saturday 30. May 2015" msgctxt "#32143" msgid "30.05.2015" -msgstr "" +msgstr "30.05.2015" msgctxt "#32144" msgid "05.30.2015" -msgstr "" +msgstr "05.30.2015" msgctxt "#32145" msgid "Custom" -msgstr "" +msgstr "自定义" # empty strings from id 32146 to 32149 msgctxt "#32150" msgid "Custom value for X position (1-1280)" -msgstr "" +msgstr "X位置的自定义值(1-1280)" # empty strings from id 32151 to 32159 msgctxt "#32160" msgid "Custom value for Y position (1-720)" -msgstr "" +msgstr "Y位置的自定义值(1-720)" # empty strings from id 32161 to 32169 msgctxt "#32170" msgid "Enter custom date format (Empty=Regional settings):" -msgstr "" +msgstr "输入自定义日期格式(空=区域设置):" msgctxt "#32171" msgid "Day: d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" -msgstr "" +msgstr "日:d(1-31) dd(01-31) ddd(Mon-Sun) DDD(Monday-Sunday)" msgctxt "#32172" msgid "Month: m(1-12) mm(01-12) mmm(Jan-Dec) MMM(January-December)" -msgstr "" +msgstr "月:m(1-12) mm(01-12) mmm(Jan-Dec) MMM(January-December)" msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" +msgstr "年:yy(2位数字年) yyyy(4位数字年)" + +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" msgstr "" # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" -msgstr "" +msgstr "启用附加信息" # empty strings from id 32201 to 32209 msgctxt "#32210" msgid "Number of seconds for information switch" -msgstr "" +msgstr "信息切换的秒数" # empty strings from id 32211 to 32219 msgctxt "#32220" msgid "Enable now playing information" -msgstr "" +msgstr "启用当前播放信息" msgctxt "#32221" msgid "Show only music information" -msgstr "" +msgstr "仅显示音乐信息" msgctxt "#32222" msgid "This will hide all other info while the music is playing!" -msgstr "" +msgstr "这将在播放音乐时隐藏所有其他信息!" msgctxt "#32223" msgid "Merge song artist and title" -msgstr "" +msgstr "合并歌手和歌名" # empty strings from id 32224 to 32229 msgctxt "#32230" msgid "Enable weather information" -msgstr "" +msgstr "启用天气信息" # empty strings from id 32231 to 32239 msgctxt "#32240" msgid "Weather icons" -msgstr "" +msgstr "天气图标" msgctxt "#32241" msgid "Hide weather icon" -msgstr "" +msgstr "隐藏天气图标" msgctxt "#32242" msgid "Icon set 1" -msgstr "" +msgstr "图标集1" msgctxt "#32243" msgid "Icon set 2" -msgstr "" +msgstr "图标集2" msgctxt "#32244" msgid "Icon set 3" -msgstr "" +msgstr "图标集3" msgctxt "#32245" msgid "Icon set 4" -msgstr "" +msgstr "图标集4" # empty strings from id 32246 to 32249 msgctxt "#32250" msgid "Enable album art" -msgstr "" +msgstr "启用专辑艺术" # empty strings from id 32251 to 32259 msgctxt "#32260" msgid "Enable CPU usage information" -msgstr "" +msgstr "启用 CPU 使用信息" msgctxt "#32261" msgid "Enable battery level information" -msgstr "" +msgstr "启用电池电量信息" msgctxt "#32262" msgid "Enable free memory information" -msgstr "" +msgstr "启用可用内存信息" msgctxt "#32263" msgid "Enable movie library information" -msgstr "" +msgstr "启用电影资料库信息" msgctxt "#32264" msgid "Enable TV show library information" -msgstr "" +msgstr "启用剧集资料库信息" msgctxt "#32265" msgid "Enable music library information" -msgstr "" +msgstr "启用音乐资料库信息" msgctxt "#32266" msgid "Enable CPU temperature" -msgstr "" +msgstr "启用 CPU 温度" msgctxt "#32267" msgid "Enable GPU temperature" -msgstr "" +msgstr "启用 GPU 温度" msgctxt "#32268" msgid "Enable HDD temperature" -msgstr "" +msgstr "启用硬盘温度" msgctxt "#32269" msgid "Enable FPS" -msgstr "" +msgstr "启用 FPS" msgctxt "#32270" msgid "Enable current uptime" -msgstr "" +msgstr "启用当前运行时长" msgctxt "#32271" msgid "Enable total uptime" -msgstr "" +msgstr "启用总运行时长" # empty strings from id 32272 to 32279 msgctxt "#32280" msgid "CPU:" -msgstr "" +msgstr "CPU:" msgctxt "#32281" msgid "Battery:" -msgstr "" +msgstr "电池:" msgctxt "#32282" msgid "Free memory:" -msgstr "" +msgstr "可用内存:" msgctxt "#32283" msgid "Total movies:" -msgstr "" +msgstr "电影总数:" msgctxt "#32284" msgid "Watched movies:" -msgstr "" +msgstr "已观看电影:" msgctxt "#32285" msgid "Unwatched movies:" -msgstr "" +msgstr "未观看电影:" msgctxt "#32286" msgid "Total TV shows:" -msgstr "" +msgstr "剧集总数:" msgctxt "#32287" msgid "Total episodes:" -msgstr "" +msgstr "总集数:" msgctxt "#32288" msgid "Watched TV shows:" -msgstr "" +msgstr "已观看剧集:" msgctxt "#32289" msgid "Watched episodes:" -msgstr "" +msgstr "已观看分集:" msgctxt "#32290" msgid "Unwatched TV shows:" -msgstr "" +msgstr "未观看剧集:" msgctxt "#32291" msgid "Unwatched episodes:" -msgstr "" +msgstr "未观看分集:" msgctxt "#32292" msgid "Artists:" -msgstr "" +msgstr "歌手:" msgctxt "#32293" msgid "Albums:" -msgstr "" +msgstr "专辑:" msgctxt "#32294" msgid "Songs:" -msgstr "" +msgstr "歌曲:" msgctxt "#32295" msgid "CPU temperature:" -msgstr "" +msgstr "CPU 温度:" msgctxt "#32296" msgid "GPU temperature:" -msgstr "" +msgstr "GPU 温度:" msgctxt "#32297" msgid "HDD temperature:" -msgstr "" +msgstr "硬盘温度:" msgctxt "#32298" msgid "FPS:" -msgstr "" +msgstr "FPS:" msgctxt "#32299" msgid "Current uptime:" -msgstr "" +msgstr "当前运行时长:" msgctxt "#32300" msgid "Total uptime:" -msgstr "" +msgstr "总运行时长:" # empty strings from id 32301 to 32309 msgctxt "#32310" msgid "Choose hour color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择小时颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.hourcolor)]]($INFO[Skin.String(screensaver.digitalclock.hourcolor.name)])[/COLOR]" msgctxt "#32311" msgid "Random hour color" -msgstr "" +msgstr "随机小时颜色" msgctxt "#32312" msgid "Random hour opacity" -msgstr "" +msgstr "随机小时不透明度" # empty strings from id 32313 to 32319 msgctxt "#32320" msgid "Choose colon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" -msgstr "" +msgstr "选择冒号颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.coloncolor)]]($INFO[Skin.String(screensaver.digitalclock.coloncolor.name)])[/COLOR]" msgctxt "#32321" msgid "Random colon color" -msgstr "" +msgstr "随机冒号颜色" msgctxt "#32322" msgid "Random colon opacity" -msgstr "" +msgstr "随机冒号不透明度" # empty strings from id 32323 to 32329 msgctxt "#32330" msgid "Choose minute color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" -msgstr "" +msgstr "选择分钟颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.minutecolor)]]($INFO[Skin.String(screensaver.digitalclock.minutecolor.name)])[/COLOR]" msgctxt "#32331" msgid "Random minute color" -msgstr "" +msgstr "随机分钟颜色" msgctxt "#32332" msgid "Random minute opacity" -msgstr "" +msgstr "随机分钟不透明度" # empty strings from id 32333 to 32339 msgctxt "#32340" msgid "Choose AM/PM color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择 AM/PM 颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.ampmcolor)]]($INFO[Skin.String(screensaver.digitalclock.ampmcolor.name)])[/COLOR]" msgctxt "#32341" msgid "Random AM/PM color" -msgstr "" +msgstr "随机 AM/PM 颜色" msgctxt "#32342" msgid "Random AM/PM opacity" -msgstr "" +msgstr "随机 AM/PM 不透明度" # empty strings from id 32343 to 32349 msgctxt "#32350" msgid "Choose date color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" -msgstr "" +msgstr "选择日期颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.datecolor)]]($INFO[Skin.String(screensaver.digitalclock.datecolor.name)])[/COLOR]" msgctxt "#32351" msgid "Random date color" -msgstr "" +msgstr "随机日期颜色" msgctxt "#32352" msgid "Random date opacity" -msgstr "" +msgstr "随机日期不透明度" # empty strings from id 32353 to 32359 msgctxt "#32360" msgid "Choose additional information color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择附加信息颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.informationcolor)]]($INFO[Skin.String(screensaver.digitalclock.informationcolor.name)])[/COLOR]" msgctxt "#32361" msgid "Random additional information color" -msgstr "" +msgstr "随机附加信息颜色" msgctxt "#32362" msgid "Random additional information opacity" -msgstr "" +msgstr "随机附加信息不透明度" # empty strings from id 32363 to 32369 msgctxt "#32370" msgid "Choose icon color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择图标颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.iconcolor)]]($INFO[Skin.String(screensaver.digitalclock.iconcolor.name)])[/COLOR]" msgctxt "#32371" msgid "Random icon color" -msgstr "" +msgstr "随机图标颜色" msgctxt "#32372" msgid "Random icon opacity" -msgstr "" +msgstr "随机图标不透明度" # empty strings from id 32373 to 32499 msgctxt "#32500" msgid "Choose text shadow color and opacity - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择文本阴影颜色和不透明度 - [COLOR $INFO[Skin.String(screensaver.digitalclock.shadowcolor)]]($INFO[Skin.String(screensaver.digitalclock.shadowcolor.name)])[/COLOR]" # empty strings from id 32501 to 32504 msgctxt "#32505" msgid "Choose background image aspect ratio:" -msgstr "" +msgstr "选择背景图像宽高比:" msgctxt "#32506" msgid "Fill the screen" -msgstr "" +msgstr "满屏" msgctxt "#32507" msgid "Original size" -msgstr "" +msgstr "原始尺寸" # empty strings from id 32508 to 32509 msgctxt "#32510" msgid "Choose background" -msgstr "" +msgstr "选择背景" msgctxt "#32511" msgid "One color" -msgstr "" +msgstr "单色" msgctxt "#32512" msgid "Single image" -msgstr "" +msgstr "单图片" msgctxt "#32513" msgid "Slideshow" -msgstr "" +msgstr "幻灯片" msgctxt "#32514" msgid "Skin Helper Backgrounds" -msgstr "" +msgstr "皮肤帮手背景" msgctxt "#32515" msgid "Dim" -msgstr "" +msgstr "变暗" msgctxt "#32516" msgid "Brightness level" -msgstr "" +msgstr "亮度等级" # empty strings from id 32517 to 32519 msgctxt "#32520" msgid "Choose background color - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" -msgstr "" +msgstr "选择背景色 - [COLOR $INFO[Skin.String(screensaver.digitalclock.backgroundcolor)]]($INFO[Skin.String(screensaver.digitalclock.backgroundcolor.name)])[/COLOR]" # empty strings from id 32521 to 32529 msgctxt "#32530" msgid "Choose image" -msgstr "" +msgstr "选择图片" msgctxt "#32531" msgid "Warning! Having only one image on your screen can cause a burn in!" -msgstr "" +msgstr "警告!屏幕只显示单一图像可能导致烧屏!" # empty strings from id 32532 to 32539 msgctxt "#32540" msgid "Choose slideshow directory" -msgstr "" +msgstr "选择幻灯片目录" # empty strings from id 32541 to 32549 msgctxt "#32550" msgid "Random images" -msgstr "" +msgstr "随机图片" # empty strings from id 32551 to 32559 msgctxt "#32560" msgid "Change background picture every:" -msgstr "" +msgstr "更改背景图片每:" msgctxt "#32561" msgid "15 sec" -msgstr "" +msgstr "15秒" msgctxt "#32562" msgid "30 sec" -msgstr "" +msgstr "30秒" msgctxt "#32563" msgid "1 min" -msgstr "" +msgstr "1分钟" msgctxt "#32564" msgid "2 min" -msgstr "" +msgstr "2分钟" msgctxt "#32565" msgid "3 min" -msgstr "" +msgstr "3分钟" msgctxt "#32566" msgid "4 min" -msgstr "" +msgstr "4分钟" msgctxt "#32567" msgid "5 min" -msgstr "" +msgstr "5分钟" msgctxt "#32568" msgid "6 min" -msgstr "" +msgstr "6分钟" msgctxt "#32569" msgid "7 min" -msgstr "" +msgstr "7分钟" msgctxt "#32570" msgid "8 min" -msgstr "" +msgstr "8分钟" msgctxt "#32571" msgid "9 min" -msgstr "" +msgstr "9分钟" msgctxt "#32572" msgid "10 min" -msgstr "" +msgstr "10分钟" # empty strings from id 32573 to 32578 msgctxt "#32579" msgid "Requires skin.helper.backgrounds!" -msgstr "" +msgstr "依赖 skin.helper.backgrounds!" msgctxt "#32580" msgid "Choose Skin Helper background" -msgstr "" +msgstr "选择皮肤助手背景" msgctxt "#32581" msgid "Movie random fanart" -msgstr "" +msgstr "电影随机同人画" msgctxt "#32582" msgid "TV show random fanart" -msgstr "" +msgstr "剧集随机同人画" msgctxt "#32583" msgid "Music artist random fanart" -msgstr "" +msgstr "歌手随机同人画" msgctxt "#32584" msgid "Random fanart of all media types" -msgstr "" +msgstr "全部媒体类型随机同人画" # empty strings from id 32585 to 32599 msgctxt "#32600" msgid "Element size increase (%)" -msgstr "" +msgstr "元素大小增加(%)" msgctxt "#32610" msgid "Log out" -msgstr "" +msgstr "注销" msgctxt "#32611" msgid "Stop now playing media" -msgstr "" +msgstr "停止当前播放媒体" msgctxt "#32612" msgid "Log out after (minutes)" -msgstr "" +msgstr "注销于(分钟)" msgctxt "#32620" msgid "Enable RSS" -msgstr "" +msgstr "启用 RSS" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" +msgstr "RSS 需要在 Kodi 中正确配置并启用!" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" msgstr "" diff --git a/screensaver.digitalclock/resources/language/resource.language.zh_tw/strings.po b/screensaver.digitalclock/resources/language/resource.language.zh_tw/strings.po index 0a4e72c85..e6379d8f8 100644 --- a/screensaver.digitalclock/resources/language/resource.language.zh_tw/strings.po +++ b/screensaver.digitalclock/resources/language/resource.language.zh_tw/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: KODI Addons\n" "Report-Msgid-Bugs-To: http://trac.kodi.tv/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2022-03-01 17:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Chinese (Taiwan) \n" "Language: zh_tw\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11\n" msgctxt "Addon Summary" msgid "Digital clock screensaver" @@ -85,15 +86,15 @@ msgstr "" msgctxt "#32111" msgid "Slow" -msgstr "" +msgstr "縵" msgctxt "#32112" msgid "Normal" -msgstr "" +msgstr "一般" msgctxt "#32113" msgid "Fast" -msgstr "" +msgstr "快" # empty strings from id 32114 to 32119 msgctxt "#32120" @@ -193,6 +194,15 @@ msgctxt "#32173" msgid "Year: yy(2-digit year) yyyy(4-digit year)" msgstr "" +# empty strings from id 32174 to 32179 +msgctxt "#32180" +msgid "17:14 No hour zero padding Windows" +msgstr "" + +msgctxt "#32181" +msgid "17:14 No hour zero padding Unix" +msgstr "" + # empty strings from id 32174 to 32199 msgctxt "#32200" msgid "Enable additional information" @@ -653,3 +663,15 @@ msgstr "" msgctxt "#32621" msgid "RSS needs to be properly configured in Kodi and enabled!" msgstr "" + +msgctxt "#32630" +msgid "Turn off screen via CEC" +msgstr "" + +msgctxt "#32631" +msgid "Stop now playing media" +msgstr "" + +msgctxt "#32632" +msgid "Turn off screen via CEC after (minutes)" +msgstr "" diff --git a/screensaver.digitalclock/resources/settings.xml b/screensaver.digitalclock/resources/settings.xml index ebae39f03..0c0490806 100644 --- a/screensaver.digitalclock/resources/settings.xml +++ b/screensaver.digitalclock/resources/settings.xml @@ -9,7 +9,7 @@ - + @@ -84,5 +84,8 @@ + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeon.nox.5.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeon.nox.5.xml deleted file mode 100644 index 0a199bde8..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.aeon.nox.5.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font15 - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15 - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15 - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq5.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq5.xml deleted file mode 100644 index 1918d917b..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq5.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_24 - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font_24 - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font_24 - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq6.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq6.xml deleted file mode 100644 index aa858fa7e..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq6.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_100 - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font_24 - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font_24 - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font_24 - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.ace2.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq7.matrix.mod.xml similarity index 100% rename from screensaver.digitalclock/resources/skins/default/720p/skin.ace2.xml rename to screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq7.matrix.mod.xml diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.matrix.mod.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.matrix.mod.xml new file mode 100644 index 000000000..c18179295 --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.matrix.mod.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 50 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Minute + 190 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + AMPM + 285 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font_lyrics + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.nexus.mod.xml similarity index 100% rename from screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.xml rename to screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq8.nexus.mod.xml diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.mod.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.mod.xml new file mode 100644 index 000000000..ad2da469c --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.mod.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 60 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Minute + 180 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + AMPM + 280 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font_lyrics + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.xml new file mode 100644 index 000000000..ad2da469c --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.aeonmq9.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 60 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + Minute + 180 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + lyr2 + + + AMPM + 280 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font_lyrics + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font_lyrics + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.2.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.mod.xml similarity index 100% rename from screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.2.xml rename to screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.mod.xml diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.xml deleted file mode 100644 index 427749f2c..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.arctic.zephyr.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - SmallBold - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - SmallBold - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - SmallBold - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.aura.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.aura.xml deleted file mode 100644 index 6ed13ae6e..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.aura.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font_small - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_small - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_small - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.bello.7.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.bello.7.xml deleted file mode 100644 index 369dbd0d3..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.bello.7.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font13_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.adonic.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.bello.8.xml similarity index 100% rename from screensaver.digitalclock/resources/skins/default/720p/skin.adonic.xml rename to screensaver.digitalclock/resources/skins/default/720p/skin.bello.8.xml diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.blackglassnova.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.blackglassnova.xml deleted file mode 100644 index 325515773..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.blackglassnova.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - OSDFont - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - OSDFont - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - OSDFont - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.box.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.box.xml deleted file mode 100644 index d3fd9eea0..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.box.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScreensaverClock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScreensaverClock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScreensaverClock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - List_PVR - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - List_PVR - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - List_PVR - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.carpc-carbon.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.carpc-carbon.xml deleted file mode 100644 index c1d4254d5..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.carpc-carbon.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font80_title - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font80_title - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font80_title - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font20_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font20_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font20_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.chroma.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.chroma.xml deleted file mode 100644 index 78d04416d..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.chroma.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScrollOffsetFont - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScrollOffsetFont - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - ScrollOffsetFont - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - InfoPlotFont - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - InfoPlotFont - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - InfoPlotFont - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.ellipsisui.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.ellipsisui.xml new file mode 100644 index 000000000..76426b75e --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.ellipsisui.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 60 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + Huge + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + Huge + + + Minute + 180 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + Huge + + + AMPM + 280 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font13_title + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font13_title + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font13_title + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.embuary-leia.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.embuary-leia.xml deleted file mode 100644 index 369dbd0d3..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.embuary-leia.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font13_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.bello.6.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.embuary-matrix.xml similarity index 100% rename from screensaver.digitalclock/resources/skins/default/720p/skin.bello.6.xml rename to screensaver.digitalclock/resources/skins/default/720p/skin.embuary-matrix.xml diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.ftv.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.ftv.xml index 4f56e8fe9..f9066f23b 100644 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.ftv.xml +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.ftv.xml @@ -1,47 +1,20 @@ - -0x00000000 + + 0xffff00ff - Background - 0 - 0 - 1280 - 720 - 500 - scale + false - Background - 0 - 0 - 1280 - 720 - 500 - keep + false + + + + FullscreenDimensions + special://skin/media/common/background_default.png - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - + false Clock and Date @@ -54,186 +27,153 @@ WindowOpen WindowOpen WindowOpen - WindowOpen + WindowOpen 460 290 360 280 true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Light144 + + + -50 + -100 + 460 + 460 + special://skin/media/common/clock_background.png + stretch - - Colon - 0 - 10 + + 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 + 280 center - center - - Light144 + 0 + horizontal + true + Conditional + + auto + 99 + Light148 + ffbfbcbb + + + 27 + 99 + Control.IsVisible(30102) + + 28 + 27 + 13 + + Light80 + center + ffbfbcbb + + + 64 + 27 + 13 + + Light80 + center + ffbfbcbb + + + + 27 + 99 + !Control.IsVisible(30102) + + 28 + 27 + 13 + + Light80 + center + 88bfbcbb + + + 64 + 27 + 13 + + Light80 + center + 88bfbcbb + + + + auto + 99 + Light148 + ffbfbcbb + + + 53 + auto + 39 + Med58 + ffbfbcbb - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Light144 - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Med28 - + - Date - 0 - 85 + 96 360 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 center - center - true - - Med28 - + Reg36Spaced + ffbfbcbb + Conditional + + - Information - 0 - 125 + 134 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 + 30 + center + Reg30Spaced + ffbfbcbb + + + + 120 + 160 + 120 + 120 center center - true - - Med28 - + keep + true + + + + Colon + 00ffffff + 00ffffff + - Hour color control - 0 - 0 - 0 - 0 - center - - font14 false - Colon color control - 0 - 0 - 0 - 0 - center - - font14 false - + - Minute color control - 0 - 0 - 0 - 0 - center - - font14 false - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 false - + - Date color control - 0 - 0 - 0 - 0 - center - - font14 false - + - Information color control - 0 - 0 - 0 - 0 - center - - font14 false - + - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 false - + - \ No newline at end of file + diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.fuse.neue.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.fuse.neue.xml deleted file mode 100644 index 9ad412133..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.fuse.neue.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font_systeminfo_skinname - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_systeminfo_skinname - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_systeminfo_skinname - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.grid.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.grid.xml deleted file mode 100644 index 369dbd0d3..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.grid.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font13_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.horizon.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.horizon.xml deleted file mode 100644 index 47306e661..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.horizon.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font_small_bold - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_small_bold - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font_small_bold - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.madnox.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.madnox.xml deleted file mode 100644 index 0c9eda9c1..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.madnox.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font120_Caps_Lyrics - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font120_Caps_Lyrics - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font120_Caps_Lyrics - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Font32_Reg - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font32_Reg - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - Font32_Reg - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.lr.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.lr.xml new file mode 100644 index 000000000..138dd781d --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.lr.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 60 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + WeatherTempFont + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + WeatherTempFont + + + Minute + 180 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + WeatherTempFont + + + AMPM + 280 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font15 + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font15 + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font15 + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.xml deleted file mode 100644 index 48fda568b..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.mimic.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font15_bold - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15_bold - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15_bold - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.nebula.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.nebula.xml deleted file mode 100644 index 7b300244e..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.nebula.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - Clock - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - LeftMenuButtonFont - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - LeftMenuButtonFont - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - LeftMenuButtonFont - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.omni.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.omni.xml deleted file mode 100644 index 48fda568b..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.omni.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font15_bold - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15_bold - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font15_bold - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.osmc.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.osmc.xml index 2b84f0ce2..ce831fec1 100644 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.osmc.xml +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.osmc.xml @@ -83,7 +83,7 @@ center center - Clock + Font120 Colon @@ -97,7 +97,7 @@ center center - Clock + Font120 Minute @@ -111,7 +111,7 @@ center center - Clock + Font120 AMPM @@ -125,7 +125,7 @@ center center - Small + Font36 Date @@ -140,7 +140,7 @@ center true - Small + Font36 Information @@ -155,7 +155,7 @@ center true - Small + Font36 Hour color control @@ -236,4 +236,4 @@ - \ No newline at end of file + diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.phenomenal.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.phenomenal.xml deleted file mode 100644 index 599ccf7c4..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.phenomenal.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font13 - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13 - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13 - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.re-touched.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.re-touched.xml deleted file mode 100644 index 5b7e8ee31..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.re-touched.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font30_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font24_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font24_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.reestuarized.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.reestuarized.xml deleted file mode 100644 index 5fe81563c..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.reestuarized.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font30_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font30_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font30_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.revolve.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.revolve.xml deleted file mode 100644 index 72f2c7bbe..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.revolve.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - HomeMenuOptionFont - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - HomeMenuOptionFont - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - HomeMenuOptionFont - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - MediumViewFont - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - MediumViewFont - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - MediumViewFont - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.tetradui.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.tetradui.xml new file mode 100644 index 000000000..d066328a9 --- /dev/null +++ b/screensaver.digitalclock/resources/skins/default/720p/skin.tetradui.xml @@ -0,0 +1,239 @@ + + +0x00000000 + + + Background + 0 + 0 + 1280 + 720 + 500 + scale + + + Background + 0 + 0 + 1280 + 720 + 500 + keep + + + + 0 + 0 + 110% + 80 + special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png + + + 0 + 0 + 40 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFFFFFFF + true + + + + Clock and Date + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + WindowOpen + 460 + 290 + 360 + 280 + true + + Weathericon + 120 + 160 + 120 + 120 + center + center + keep + true + + + Hour + 60 + 10 + 120 + 65 + $INFO[Control.GetLabel(30105)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font60 + + + Colon + 0 + 10 + 360 + 65 + $INFO[Control.GetLabel(30106)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font60 + + + Minute + 180 + 10 + 120 + 65 + $INFO[Control.GetLabel(30107)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font60 + + + AMPM + 280 + 45 + 70 + 35 + $INFO[Control.GetLabel(30108)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + + font12 + + + Date + 0 + 85 + 360 + 35 + $INFO[Control.GetLabel(30109)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font12 + + + Information + 0 + 125 + 360 + 35 + $INFO[Control.GetLabel(30111)] + $INFO[Control.GetLabel(30112)] + 00000000 + center + center + true + + font12 + + + Hour color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Colon color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Minute color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + AMPM color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Date color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Information color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + Shadow color control + 0 + 0 + 0 + 0 + center + + font14 + false + + + + \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.titan.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.titan.xml deleted file mode 100644 index 3c126a372..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.titan.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font13_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font13_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.transparency.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.transparency.xml deleted file mode 100644 index e8a100736..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.transparency.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-80 - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font-28 - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font-22 - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font-22 - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.digitalclock/resources/skins/default/720p/skin.xperience1080.xml b/screensaver.digitalclock/resources/skins/default/720p/skin.xperience1080.xml deleted file mode 100644 index 780426ff5..000000000 --- a/screensaver.digitalclock/resources/skins/default/720p/skin.xperience1080.xml +++ /dev/null @@ -1,239 +0,0 @@ - - -0x00000000 - - - Background - 0 - 0 - 1280 - 720 - 500 - scale - - - Background - 0 - 0 - 1280 - 720 - 500 - keep - - - - 0 - 0 - 110% - 80 - special://home/addons/screensaver.digitalclock/resources/media/InfoBar.png - - - 0 - 0 - 40 - 100% - font12 - 1 - - button_focus - button_focus - text_shadow - FFFFFFFF - true - - - - Clock and Date - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - WindowOpen - 460 - 290 - 360 - 280 - true - - Weathericon - 120 - 160 - 120 - 120 - center - center - keep - true - - - Hour - 60 - 10 - 120 - 65 - $INFO[Control.GetLabel(30105)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Colon - 0 - 10 - 360 - 65 - $INFO[Control.GetLabel(30106)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - Minute - 180 - 10 - 120 - 65 - $INFO[Control.GetLabel(30107)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - WeatherTemp - - - AMPM - 280 - 45 - 70 - 35 - $INFO[Control.GetLabel(30108)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - - font24_title - - - Date - 0 - 85 - 360 - 35 - $INFO[Control.GetLabel(30109)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font24_title - - - Information - 0 - 125 - 360 - 35 - $INFO[Control.GetLabel(30111)] - $INFO[Control.GetLabel(30112)] - 00000000 - center - center - true - - font24_title - - - Hour color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Colon color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Minute color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - AMPM color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Date color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Information color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - Shadow color control - 0 - 0 - 0 - 0 - center - - font14 - false - - - - \ No newline at end of file diff --git a/screensaver.weather/addon.xml b/screensaver.weather/addon.xml new file mode 100644 index 000000000..1e31cc76d --- /dev/null +++ b/screensaver.weather/addon.xml @@ -0,0 +1,28 @@ + + + + + + + + just display the weather dialog + zeige einfach das Wetter an + just display the weather dialog + + zeige einfach das Wetter an + + en + de + all + MIT + https://github.com/Heckie75/kodi-addon-screensaver-weather + https://github.com/Heckie75/kodi-addon-screensaver-weather + +v1.0.0 (2024-02-25) +- initial version + + + resources/assets/icon.png + + + diff --git a/screensaver.weather/default.py b/screensaver.weather/default.py new file mode 100644 index 000000000..3e2b313c0 --- /dev/null +++ b/screensaver.weather/default.py @@ -0,0 +1,5 @@ +import xbmc + +if __name__ == "__main__": + + xbmc.executebuiltin("ActivateWindow(Weather)") diff --git a/screensaver.weather/resources/assets/icon.png b/screensaver.weather/resources/assets/icon.png new file mode 100644 index 000000000..28808d2f1 Binary files /dev/null and b/screensaver.weather/resources/assets/icon.png differ diff --git a/script.artistslideshow/addon.xml b/script.artistslideshow/addon.xml index 203a7d776..2dd8793f8 100644 --- a/script.artistslideshow/addon.xml +++ b/script.artistslideshow/addon.xml @@ -1,9 +1,8 @@ - + - @@ -11,9 +10,8 @@ -v.3.3.3 -- trim_cache now deletes information as well as images -- update to new settings format +v.3.3.7 +- fix to get fourth artwork from theAudioDB icon.png @@ -24,7 +22,6 @@ v.3.3.3 https://kodi.wiki/index.php?title=Add-on:Artist_Slideshow artistslideshow.xbmc@gmail.com https://github.com/pkscout/script.artistslideshow - حمل المزيد من الصور والمعلومات عن الفنان الذى يُغنِى حالياً Сваля изображения и допълнителна инф. за изпълнителя на текущата песен Descàrrega imatges i informació addicional de l'artista que s'està reproduint Download billeder og ekstra info om kunstneren, der spiller i øjeblikket @@ -32,15 +29,18 @@ v.3.3.3 Κατεβάστε εικόνες και επιπλέον πληροφορίες του τρέχοντος καλλιτέχνη Download images and additional info of the currently playing artist Download images and additional info of the currently playing artist + Download images and additional info of the currently playing artist + Download images and additional info of the currently playing artist Download images and additional info of the currently playing artist Bajá imágenes e información adicional sobre el artista Descarga imágenes e información adicional del intérprete que esté sonando Descargar imágenes e información adicional del artista actualmente en reproducción + Lae alla pilte ja lisateavet hetkel mängiva esitaja kohta Noutaa kuvia ja lisätietoja parhaillaan toistettavasta esittäjästä Télécharger des images et des infos additionnelles sur l'artiste écouté + Télécharger des images et des infos additionnelles sur l'artiste écouté Télécharger les images et les informations additionnelles de l'artiste en cours de lecture Descarga imaxes e info. adicional do artista que está a soar - הורד תמונות ומידע נוסף על האמן המושמע כעת Preuzimite slike i dodatne informacije o trenutno reproduciranom izvođaču Képeket és kiegészítő információkat tölt le a jelenleg játszott előadóról Scarica immagini e informazioni aggiuntive dell'artista attualmente in riproduzione @@ -50,13 +50,11 @@ v.3.3.3 Downloaden van afbeeldingen en extra informatie van te horen artiest Pobieraj obrazy i dodatkowe informacje o aktualnie odtwarzanym wykonawcy Transferência de imagens e informações adicionais de artistas que estejam sendo reproduzidos - Transferir imagens e info adicional do artista em reprodução agora Загружает изображения и дополнительную информацию об исполнителе, который воспроизводится в данный момент Stiahnuť obrázky a dodatočné informácie o práve hrajúcich interpretoch Naložite slike in dodatne informacije, za trenutno predvajanega izvajalca Ladda ner bilder samt ytterligare information om den spelade artisten 下载当前播放艺人的图片和附加信息 - ملحق يساعدك على تَنِزِيل صور ومعلومات إضافيه عن الفنان الذى تستمع اليه حالياً من موقع fanart.tv و theaudiodb.com. كما يمكنك جعل جلد واجهتك الحالى يستخدم كل ما سبق ﻹنشاء عروض شرائح للفنان الذى تستمع إليه Добавката сваля картинки и допълнителна информация от www.fanart.tv и www.theaudiodb.com за изпълнителя който звучи в момента. Картинките, заедно с фанарт изображенията на изпълнителя, може да се ползват от облика, за създаването на слайдшоу за изпълнителя, който звучи в момента. Complement per descarregar imatges i informació addicional de fanart.tv i theaudiodb.com de l'artista que s'està reproduint. Les imatges, juntament amb les imatges locals d'artistes, i la informació, pot ser utilitzada per la pell per crear una presentació de diapositives per a l'artista que s'està escoltant. Add-on til at downloade billeder og yderligere information fra fanart.tv og theaudiodb.com af den aktuelt afspillede kunstner. Billederne kan sammen med lokale kunstneres billeder og info bruges af dit skin til at skabe et diasshow for kunstneren, der lyttes til. @@ -64,23 +62,25 @@ v.3.3.3 Πρόσθετο για τη λήψη εικόνων και επιπλέον πληροφοριών από το fanart.tv και το theaudiodb.com για τον τρέχοντα καλλιτέχνη. Οι εικόνες, μαζί με τοπικές εικόνες του καλλιτέχνη καθώς και πληροφορίες, μπορούν να χρησιμοποιηθούν από το κέλυφος για τη δημιουργία παρουσιάσεων διαφανειών του καλλιτέχνη η μουσική του οποίου ακούγεται. Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to. Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to. + Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to. + Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to. Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to. Addon para descargar imágenes e información adicional de fanart.tv y theaudiodb.com del intérprete que se está reproduciendo. Las imágenes, junto con imágenes de intérpretes locales, y la información puede ser utilizada por la piel para crear una presentación de diapositivas para el intérprete que se está escuchando. Complemento para descargar imágenes e información adicional de fanart.tv y theaudiodb.com del artista actualmente en reproducción. Las imágenes, junto con las imágenes e información locales de los artistas pueden ser utilizados por la máscara para crear una presentación del artista que estés escuchando. + Lisamoodul, millega saab alla laadida pilte ja lisateavet fanart.tv ja theaudiodb.com saitidelt hetkel mängiva esitaja kohta. Koos meediakogus leiduvate esitajate piltide ja teabega saab rüü neid kasutada kuulatava esitaja slaidiseansi loomiseks. Lisäosa parhaillaan kuunneltavan esittäjän kuvien ja tietojen noutoon mm. Fanart.tv- ja TheAudioDB.com-palveluista. Ulkoasut voivat luoda kuvaesityksiä kuunneltavasta esittäjästä käyttäen näitä tietoja ja esittäjien paikallisia kuvia. Addiciel pour télécharger des images et des informations additionnelles sur fanart.tv et theaudiodb.com de l'artiste en cours d'écoute. Les images, ainsi que les images locales de l'artiste, et les infos peuvent être utilisées par l'habillage pour créer un diaporama pour l'artiste en cours d'écoute. + Addiciel pour télécharger des images et des informations additionnelles sur fanart.tv et theaudiodb.com de l'artiste en cours d'écoute. Les images, ainsi que les images locales de l'artiste, et les infos peuvent être utilisées par l'habillage pour créer un diaporama pour l'artiste en cours d'écoute. Extension permettant de télécharger des images et des informations supplémentaires depuis fanart.tv et depuis theaudiodb.com de l'artiste en cours d'écoute. Les images téléchargées, les images locales des artistes et les informations peuvent être utilisées par le skin pour créer un diaporama pour l'artiste écouté. Complemento para descargar imaxes e información adicional de fanart.tv e theaudiodb.com do artista que está a soar. Estas imaxes, xunto cas do artista gardadas en local e a información pódeas usar o tema para crear unha presentación. - הרחבה להורדת תמונות ומידע נוסף על האמן המושמע כעת מ-fanart.tv ו- theaudiodb.com. התמונות, בנוסף לתמונות אמנים מקומיות והמידע על אמן יכולים לשמש את המעטפת ליצירת מצגת עבור האמן המושמע כעת. Dodatak za preuzimanje slika i dodatnih informacije trenutno reproduciranog izvođača s fanart.tv, theaudiodb.com i theaudiodb.com. Slike, zajedno s lokalnim slikama izvođača i informacijama mogu se koristiti s presvlakom za stvaranje slikovne prezentacije izvođača koji se sluša. A Kiegészítő letölt képeket és infromációkat a fanart.tv és theaudiodb.com oldalakról az éppen hallgatott előadóhoz. A képek a helyben tárolt képekkel együtt felhasználhatóak a Felszín által diavetítéshez. - Addon per scaricare immagini ed informazioni aggiuntive da fanart.tv e theaudiodb.com sull'artista attualmente in esecuzione. Le immagini possono essere usate dallo skin, insieme a quelle locali, per creare una presentazione per l'artista che si sta ascoltando. + Add-on per scaricare immagini ed informazioni aggiuntive da fanart.tv e theaudiodb.com sull'artista attualmente in riproduzione. Le immagini possono essere usate dalla skin, insieme a quelle locali, per creare una presentazione per l'artista che si sta ascoltando. 현재 재생중인 아티스트의 이미지와 추가정보를 fanart.tv 와 theaudiodb.com 에서 다운로드합니다. 다운로드한 이미지와 정보는 로컬 이미지와 함께 현재 재생중인 음악의 아티스트 슬라이드쇼에 사용할 수 있습니다. Tillegg for å laste ned bilder og ytterligere informasjon fra fanart.tv og theaudiodb.com for artisten som spilles av for øyeblikket. Bildene, sammen med lokale artistbilder og info, kan bli brukt av skallet til å lage en lysbildefremvisning for artisten som blir lyttet til for øyeblikket. Add-on om afbeeldingen en bijkomende informatie van Fanart.tv en theaudiodb.com van de te horen artiest binnen te halen. de afbeeldingen, net als de lokaal aanwezige artiest-afbeeldingen en -info kunnen gebruikt worden door de skin om een diashow te tonen van de artiest di nu beluisterd wordt. Wtyczka pobiera obrazy i dodatkowe informacje z fanart.tv oraz theaudiodb.com o aktualnie odtwarzanym wykonawcy. Obrazami wraz z informacjami o wykonawcach, mogą być wykorzystywane przez skórę do tworzenia pokazu slajdów wykonawcy, którego aktualnie słuchasz. Addon para download de imagens e informações adicionais do artista que está sendo reproduzido provido por fanart.tv e theaudiodb. Tais dados e imagens serão usados pelas skins para criarem um slideshow dos artistas quando estivermos escutando-os. - Um add-on para transferir imagens e informação adicional do artista em reprodução no momento, a partir de fanart.tv e theaudiodb.com. As imagens e a informação, assim como as imagens locais dos artistas, podem ser usadas pelo tema para criar uma apresentação de slides do artista em escuta no momento. Дополнение для загрузки изображений и дополнительной информации о воспроизводимом исполнителе с ресурсов fanart.tv and theaudiodb.com. Загруженные изображения и доп. информация, наравне с локальными, могут быть использованы для создания слайд-шоу во время прослушивания композиций. Dodatek za prenos slik in dodatnih informacij iz fanart.tv in theaudiodb.com za trenutno predvajanega izvajalca. Slike, skupaj z lokalnimi slikami izvajalcev ter informacije, se lahko uporabljajo s preobleko za ustvarjanje diaprojekcije slik izvajalca, ki ga trenutno poslušate. Tillägg för att ladda ner bilder och tilläggsinformation från fanart.tv och theaudiodb.com om artisten som spelas för tillfället. Bilderna, tillsammans med lokala artistbilder och information, kan användas av skinnet för att skapa ett bildspel av artisten man lyssnar på. diff --git a/script.artistslideshow/changelog.txt b/script.artistslideshow/changelog.txt index a85a19a70..a7da1ba19 100644 --- a/script.artistslideshow/changelog.txt +++ b/script.artistslideshow/changelog.txt @@ -1,3 +1,16 @@ +v.3.3.7 +- fix to get fourth artwork from theAudioDB + +v.3.3.6 +- fix to allow blank as illegal replace character in Nexus +- updated language files + +v.3.3.5 +- fix for AS not accepting images with uppcase extensions + +v.3.3.4 +- removed Leia to Matrix transition code + v.3.3.3 - trim_cache now deletes information as well as images - update to new settings format diff --git a/script.artistslideshow/resources/language/resource.language.ar_sa/strings.po b/script.artistslideshow/resources/language/resource.language.ar_sa/strings.po index 9424b0537..c6f808ec1 100644 --- a/script.artistslideshow/resources/language/resource.language.ar_sa/strings.po +++ b/script.artistslideshow/resources/language/resource.language.ar_sa/strings.po @@ -19,11 +19,11 @@ msgstr "" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" -msgstr "حمل المزيد من الصور والمعلومات عن الفنان الذى يُغنِى حالياً" +msgstr "" msgctxt "Addon Description" msgid "Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to." -msgstr "ملحق يساعدك على تَنِزِيل صور ومعلومات إضافيه عن الفنان الذى تستمع اليه حالياً من موقع fanart.tv و theaudiodb.com. كما يمكنك جعل جلد واجهتك الحالى يستخدم كل ما سبق ﻹنشاء عروض شرائح للفنان الذى تستمع إليه" +msgstr "" # Section Headings msgctxt "#32000" diff --git a/script.artistslideshow/resources/language/resource.language.de_de/strings.po b/script.artistslideshow/resources/language/resource.language.de_de/strings.po index 2320774b5..165ce11c4 100644 --- a/script.artistslideshow/resources/language/resource.language.de_de/strings.po +++ b/script.artistslideshow/resources/language/resource.language.de_de/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2021-07-18 09:29+0000\n" +"PO-Revision-Date: 2023-02-06 23:15+0000\n" "Last-Translator: Kai Sommerfeld \n" "Language-Team: German \n" "Language: de_de\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.1\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -94,7 +94,7 @@ msgstr "Client-API-Key" msgctxt "#32110" msgid "I donate to this service" -msgstr "Ich spende für diesen Service" +msgstr "Ich spende für diesen Dienst" msgctxt "#32111" msgid "Use fallback slideshow" @@ -118,11 +118,11 @@ msgstr "Album-Fanart von Kodi einschließen" msgctxt "#32116" msgid "Limit size of download cache" -msgstr "Größe des Download-Caches begrenzen" +msgstr "Größe des Downloadcaches begrenzen" msgctxt "#32117" msgid "Maximum cache size (in megabytes)" -msgstr "Maximale Cache-Größe (in Megabytes)" +msgstr "Maximale Cachegröße (in Megabytes)" msgctxt "#32118" msgid "Alternate name for local fanart folder" @@ -134,7 +134,7 @@ msgstr "Debug-Protokollierung aktivieren" msgctxt "#32120" msgid "Disable secondary and featured artist images" -msgstr "Sekundäre und vorgestellte Interpertenbilder deaktivieren" +msgstr "Sekundäre und vorgestellte Interpretenbilder deaktivieren" msgctxt "#32121" msgid "Store artist images in" @@ -190,7 +190,7 @@ msgstr "macOS" msgctxt "#32134" msgid "Move images to Kodi music artist folder" -msgstr "Bilder in Kodis Musikinterpreten-Ordner verschieben" +msgstr "Bilder in Kodis Musikinterpretenordner verschieben" msgctxt "#32135" msgid "Get artist bio from Kodi" @@ -210,11 +210,11 @@ msgstr "Schwarzblende beim Interpretenwechsel" msgctxt "#32139" msgid "Main thread sleep time (in seconds)" -msgstr "Wartezeit des Haupt-Threads (in Sekunden)" +msgstr "Wartezeit des Hauptthreads (in Sekunden)" msgctxt "#32140" msgid "Main thread idle sleep time (in seconds)" -msgstr "Leerlauf-Wartezeit des Haupt-Threads (in Sekunden)" +msgstr "Leerlaufwartezeit des Hauptthreads (in Sekunden)" msgctxt "#32141" msgid "Slideshow thread sleep time (in seconds)" @@ -222,7 +222,7 @@ msgstr "Wartezeit des Diashow-Threads (in Sekunden)" msgctxt "#32142" msgid "Show notification during image downloads" -msgstr "Benachrichtigung während Herunterladen von Bildern anzeigen" +msgstr "Benachrichtigung beim Herunterladen von Bildern anzeigen" msgctxt "#32143" msgid "Only show notification if there are images to download" @@ -266,11 +266,11 @@ msgstr "Herunterladen abgeschlossen" # Dialog texts msgctxt "#32300" msgid "This will move and rename your image files and cannot be undone. Are you sure you want to do this?" -msgstr "Dies wird die Bilddateien verschieben und umbenennen und kann nicht rückgängig gemacht werden. Fortfahren?" +msgstr "Dies verschiebt und benennt die Bilddateien um und kann nicht rückgängig gemacht werden. Fortfahren?" msgctxt "#32301" msgid "Unable to retrieve path to Kodi music artist image storage. Please check Settings > Media > Music and ensure the Artist Information Folder is set." -msgstr "Pfad für Kodis Musikinterpreten-Bilderspeicherort kann nicht ermittelt werden. Bitte prüfen, dass unter Einstellungen > Medien > Musik der Ordner für Interpreteninformationen festgelegt ist." +msgstr "Pfad für Kodis Speicherort der Musikinterpretenbilder kann nicht ermittelt werden. Bitte prüfen, dass unter Einstellungen > Medien > Musik der Ordner für Interpreteninformationen festgelegt ist." msgctxt "#32302" msgid "Artist Slideshow is already set to use the Kodi artist information folder. Please set to use either addon_data folder or a custom location and try again." @@ -278,11 +278,11 @@ msgstr "Artist Slideshow ist bereits konfiguriert, Kodis Interpreteninformatione msgctxt "#32303" msgid "Moving and renaming images from custom location..." -msgstr "Verschieben und Umbennenen der Bilder aus benutzerdefiertem Ordner ..." +msgstr "Verschieben und Umbenennen der Bilder aus benutzerdefiertem Ordner ..." msgctxt "#32304" msgid "Moving and renaming images from addon_data folder..." -msgstr "Verschieben und Umbennenen der Bilder aus Ordner „addon_data“ ..." +msgstr "Verschieben und Umbenennen der Bilder aus Ordner „addon_data“ …" msgctxt "#32305" msgid "No artist directories found." diff --git a/script.artistslideshow/resources/language/resource.language.en_au/strings.po b/script.artistslideshow/resources/language/resource.language.en_au/strings.po index 6ddd720ed..ecb83b557 100644 --- a/script.artistslideshow/resources/language/resource.language.en_au/strings.po +++ b/script.artistslideshow/resources/language/resource.language.en_au/strings.po @@ -7,15 +7,14 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-09 14:05+0000\n" -"Last-Translator: Christian Gade \n" -"Language-Team: English (Australia) \n" -"Language: en_au\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_AU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -130,7 +129,7 @@ msgstr "" msgctxt "#32119" msgid "Enable debug logging" -msgstr "Enable debug logging" +msgstr "" msgctxt "#32120" msgid "Disable secondary and featured artist images" @@ -248,7 +247,7 @@ msgstr "" msgctxt "#32202" msgid "Error" -msgstr "Error" +msgstr "" msgctxt "#32203" msgid "Progress" diff --git a/script.artistslideshow/resources/language/resource.language.en_nz/strings.po b/script.artistslideshow/resources/language/resource.language.en_nz/strings.po index d7462ed26..7a35e0557 100644 --- a/script.artistslideshow/resources/language/resource.language.en_nz/strings.po +++ b/script.artistslideshow/resources/language/resource.language.en_nz/strings.po @@ -7,15 +7,14 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-09 14:05+0000\n" -"Last-Translator: Christian Gade \n" -"Language-Team: English (New Zealand) \n" -"Language: en_nz\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_NZ\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -130,7 +129,7 @@ msgstr "" msgctxt "#32119" msgid "Enable debug logging" -msgstr "Enable debug logging" +msgstr "" msgctxt "#32120" msgid "Disable secondary and featured artist images" @@ -248,7 +247,7 @@ msgstr "" msgctxt "#32202" msgid "Error" -msgstr "Error" +msgstr "" msgctxt "#32203" msgid "Progress" diff --git a/script.artistslideshow/resources/language/resource.language.en_us/strings.po b/script.artistslideshow/resources/language/resource.language.en_us/strings.po index 077143fd2..bcdb25bc2 100644 --- a/script.artistslideshow/resources/language/resource.language.en_us/strings.po +++ b/script.artistslideshow/resources/language/resource.language.en_us/strings.po @@ -7,15 +7,14 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-11 00:15+0000\n" -"Last-Translator: Christian Gade \n" -"Language-Team: English (United States) \n" -"Language: en_us\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -130,7 +129,7 @@ msgstr "" msgctxt "#32119" msgid "Enable debug logging" -msgstr "Enable debug logging" +msgstr "" msgctxt "#32120" msgid "Disable secondary and featured artist images" @@ -248,7 +247,7 @@ msgstr "" msgctxt "#32202" msgid "Error" -msgstr "Error" +msgstr "" msgctxt "#32203" msgid "Progress" diff --git a/script.artistslideshow/resources/language/resource.language.es_es/strings.po b/script.artistslideshow/resources/language/resource.language.es_es/strings.po index daa0c7fd0..dfd4d710c 100644 --- a/script.artistslideshow/resources/language/resource.language.es_es/strings.po +++ b/script.artistslideshow/resources/language/resource.language.es_es/strings.po @@ -5,17 +5,17 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-01 17:13+0000\n" -"Last-Translator: Alfonso Cachero \n" +"PO-Revision-Date: 2024-01-07 00:13+0000\n" +"Last-Translator: José Antonio Alvarado \n" "Language-Team: Spanish (Spain) \n" "Language: es_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11\n" +"X-Generator: Weblate 5.3\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -142,7 +142,7 @@ msgstr "Almacenar imágenes de intérprete en" msgctxt "#32122" msgid "addon_data folder" -msgstr "addon_data folder" +msgstr "carpeta addon_data" msgctxt "#32123" msgid "Kodi artist information folder" @@ -230,7 +230,7 @@ msgstr "Solo mostrar notificación si hay imágenes para descargar" msgctxt "#32144" msgid "Use more agressive artist search for streams" -msgstr "Usar una búsqueda de intérpretes más agresiva para streams" +msgstr "Utilizar una búsqueda de artistas más agresiva para las transmisiones" msgctxt "#32145" msgid "Pause slideshow on playback pause" @@ -244,7 +244,7 @@ msgstr "Artist Slideshow" msgctxt "#32201" msgid "Warning" -msgstr "Precaución" +msgstr "Advertencia" msgctxt "#32202" msgid "Error" diff --git a/script.artistslideshow/resources/language/resource.language.et_ee/strings.po b/script.artistslideshow/resources/language/resource.language.et_ee/strings.po index 522766446..fd56f613d 100644 --- a/script.artistslideshow/resources/language/resource.language.et_ee/strings.po +++ b/script.artistslideshow/resources/language/resource.language.et_ee/strings.po @@ -5,25 +5,25 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-11 00:15+0000\n" -"Last-Translator: Christian Gade \n" +"PO-Revision-Date: 2023-02-27 11:31+0000\n" +"Last-Translator: rimasx \n" "Language-Team: Estonian \n" "Language: et_ee\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11.2\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" -msgstr "" +msgstr "Lae alla pilte ja lisateavet hetkel mängiva esitaja kohta" msgctxt "Addon Description" msgid "Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to." -msgstr "" +msgstr "Lisamoodul, millega saab alla laadida pilte ja lisateavet fanart.tv ja theaudiodb.com saitidelt hetkel mängiva esitaja kohta. Koos meediakogus leiduvate esitajate piltide ja teabega saab rüü neid kasutada kuulatava esitaja slaidiseansi loomiseks." # Section Headings msgctxt "#32000" @@ -40,15 +40,15 @@ msgstr "Pildid" msgctxt "#32003" msgid "Album Info" -msgstr "" +msgstr "Albumi info" msgctxt "#32004" msgid "Artist Bio" -msgstr "" +msgstr "Esitaja elulugu" msgctxt "#32005" msgid "Similar Artists" -msgstr "" +msgstr "Sarnased esitajad" msgctxt "#32006" msgid "Advanced" @@ -58,75 +58,75 @@ msgstr "Täpsem" # Settings msgctxt "#32101" msgid "Download images from fanart.tv" -msgstr "" +msgstr "Hangi pilte saidilt fanart.tv" msgctxt "#32102" msgid "Download images from theaudiodb.com" -msgstr "" +msgstr "Hangi pilte saidilt theaudiodb.com" msgctxt "#32103" msgid "Download album info from the audiodb.com" -msgstr "" +msgstr "Hangi albumiteave saidilt audiodb.com" msgctxt "#32104" msgid "Download album info from the last.fm" -msgstr "" +msgstr "Hangi albumiteave saidilt last.fm" msgctxt "#32105" msgid "Download artist bio from theaudiodb.com" -msgstr "" +msgstr "Hangi esitaja elulugu saidilt theaudiodb.com" msgctxt "#32106" msgid "Download artist bio from last.fm" -msgstr "" +msgstr "Hangi esitaja elulugu saidilt last.fm" msgctxt "#32107" msgid "Download similar artists from last.fm" -msgstr "" +msgstr "Hangi sarnaseid esitajaid saidilt last.fm" msgctxt "#32108" msgid "Download all images regardless of resolution" -msgstr "" +msgstr "Laadi alla kõik pildid sõltumata eraldusvõimest" msgctxt "#32109" msgid "Client API key" -msgstr "" +msgstr "Kliendi API võti" msgctxt "#32110" msgid "I donate to this service" -msgstr "" +msgstr "Annetan sellele teenusele" msgctxt "#32111" msgid "Use fallback slideshow" -msgstr "" +msgstr "Kasuta varuslaidiseanssi" msgctxt "#32112" msgid "Use override slideshow" -msgstr "" +msgstr "Kasuta asendusslaidiseanssi" msgctxt "#32113" msgid "Custom location" -msgstr "" +msgstr "Kohandatud asukoht" msgctxt "#32114" msgid "Include Kodi artist fanart" -msgstr "" +msgstr "Kaasa Kodi esitaja fännipldid" msgctxt "#32115" msgid "Include Kodi album fanart" -msgstr "" +msgstr "Kaasa Kodi albumi fännipildid" msgctxt "#32116" msgid "Limit size of download cache" -msgstr "" +msgstr "Piira allalaadimise vahemälu suurust" msgctxt "#32117" msgid "Maximum cache size (in megabytes)" -msgstr "" +msgstr "Maksimaalne vahemälu suurus (megabaitides)" msgctxt "#32118" msgid "Alternate name for local fanart folder" -msgstr "" +msgstr "Kohaliku fännipildi kausta alternatiivne nimi" msgctxt "#32119" msgid "Enable debug logging" @@ -134,117 +134,117 @@ msgstr "Luba silumise logimine" msgctxt "#32120" msgid "Disable secondary and featured artist images" -msgstr "" +msgstr "Keela teisesed ja esiletõstetud esitaja pildid" msgctxt "#32121" msgid "Store artist images in" -msgstr "" +msgstr "Salvesta esitaja pildid asukohta" msgctxt "#32122" msgid "addon_data folder" -msgstr "" +msgstr "addon_data kaust" msgctxt "#32123" msgid "Kodi artist information folder" -msgstr "" +msgstr "Kodi esitaja teabe kaust" msgctxt "#32124" msgid "Priority" -msgstr "" +msgstr "Prioriteet" msgctxt "#32125" msgid "Store artist information in" -msgstr "" +msgstr "Salvesta esitaja teave asukohta" msgctxt "#32126" msgid "Preferred language for artist bio" -msgstr "" +msgstr "Eelistatud keel esitaja eluloo jaoks" msgctxt "#32127" msgid "Use extrafanart folder" -msgstr "" +msgstr "Kasuta extrafanart kausta" msgctxt "#32128" msgid "Platform for image storage" -msgstr "" +msgstr "Piltide salvestamise platvorm" msgctxt "#32129" msgid "Replace illegal path characters in artist name with" -msgstr "" +msgstr "Asenda esitaja nimes olevad keelatud märgid märgiga" msgctxt "#32130" msgid "Replace trailing period in artist name with" -msgstr "" +msgstr "Asenda esitaja nime lõpus olevad punktid märgiga" msgctxt "#32131" msgid "Windows" -msgstr "" +msgstr "Windows" msgctxt "#32132" msgid "Other" -msgstr "" +msgstr "Muu" msgctxt "#32133" msgid "MacOS" -msgstr "" +msgstr "MacOS" msgctxt "#32134" msgid "Move images to Kodi music artist folder" -msgstr "" +msgstr "Liiguta pildid Kodi muusika kataloogi" msgctxt "#32135" msgid "Get artist bio from Kodi" -msgstr "" +msgstr "Hangi esitaja elulugu Kodist" msgctxt "#32136" msgid "Custom location" -msgstr "" +msgstr "Kohandatud asukoht" msgctxt "#32137" msgid "Delay between slide transitions (in seconds)" -msgstr "" +msgstr "Slaidiseansi pildi vahetamise vahemik (sekundites)" msgctxt "#32138" msgid "Fade to black during change in artists" -msgstr "" +msgstr "Esitajate vahetamisel tuhmub mustaks" msgctxt "#32139" msgid "Main thread sleep time (in seconds)" -msgstr "" +msgstr "Pealõnga puhkeaeg (sekundites)" msgctxt "#32140" msgid "Main thread idle sleep time (in seconds)" -msgstr "" +msgstr "Põhilõnga jõudeoleku aeg (sekundites)" msgctxt "#32141" msgid "Slideshow thread sleep time (in seconds)" -msgstr "" +msgstr "Slaidiseansi lõime puhkeaeg (sekundites)" msgctxt "#32142" msgid "Show notification during image downloads" -msgstr "" +msgstr "Kuva märguanne piltide allalaadimise ajal" msgctxt "#32143" msgid "Only show notification if there are images to download" -msgstr "" +msgstr "Kuva märguanne ainult siis, kui pilte allalaadimiseks leidub" msgctxt "#32144" msgid "Use more agressive artist search for streams" -msgstr "" +msgstr "Kasuta meediavoogude jaoks agressiivsemat esineja otsingut" msgctxt "#32145" msgid "Pause slideshow on playback pause" -msgstr "" +msgstr "Peata slaidiseanss taasesituse pausimisel" # empty strings from id 32145 to 32199 # Dialog headings msgctxt "#32200" msgid "Artist Slideshow" -msgstr "" +msgstr "Esitaja slaidiseanss" msgctxt "#32201" msgid "Warning" -msgstr "" +msgstr "Hoiatus" msgctxt "#32202" msgid "Error" @@ -252,87 +252,87 @@ msgstr "Viga" msgctxt "#32203" msgid "Progress" -msgstr "" +msgstr "Edenemine" msgctxt "#32204" msgid "Downloading" -msgstr "" +msgstr "Allalaadimine" msgctxt "#32205" msgid "Download Complete" -msgstr "" +msgstr "Allalaadimine on lõpetatud" # empty strings from id 32206 to 32299 # Dialog texts msgctxt "#32300" msgid "This will move and rename your image files and cannot be undone. Are you sure you want to do this?" -msgstr "" +msgstr "See teisaldab ja nimetab pildifailid ümber ning seda ei saa tagasi võtta. Kas oled kindel, et soovid seda teha?" msgctxt "#32301" msgid "Unable to retrieve path to Kodi music artist image storage. Please check Settings > Media > Music and ensure the Artist Information Folder is set." -msgstr "" +msgstr "Kodi muusika esitajate piltide salvestuskohta ei õnnestunud tuua. Vali Seaded > Meedia > Muusika ja veendu, et esitajateabe jaoks on kaust määratud." msgctxt "#32302" msgid "Artist Slideshow is already set to use the Kodi artist information folder. Please set to use either addon_data folder or a custom location and try again." -msgstr "" +msgstr "Esitaja slaidiseanss on juba seadistatud kasutama Kodi esitaja teabekausta. Määra kas addon_data kaust või kohandatud asukoht ja proovi uuesti." msgctxt "#32303" msgid "Moving and renaming images from custom location..." -msgstr "" +msgstr "Piltide teisaldamine ja ümbernimetamine kohandatud asukohast..." msgctxt "#32304" msgid "Moving and renaming images from addon_data folder..." -msgstr "" +msgstr "Piltide teisaldamine ja ümbernimetamine addon_data kaustast..." msgctxt "#32305" msgid "No artist directories found." -msgstr "" +msgstr "Esitaja katalooge ei leitud." msgctxt "#32306" msgid "Image move complete. You may now change the Artist Slideshow storage location to Kodi artist information folder." -msgstr "" +msgstr "Pildi teisaldamine on lõpetatud. Nüüd võid muuta esitaja slaidiseansi salvestuskoha Kodi esitaja teabekaustaks." msgctxt "#32307" msgid "Downloading new artist images" -msgstr "" +msgstr "Esitajale uute piltide allalaadimine" msgctxt "#32308" msgid "artist images downloaded" -msgstr "" +msgstr "esitaja pildid alla laetud" msgctxt "#32309" msgid "artist image downloaded" -msgstr "" +msgstr "esitaja pilt alla laetud" # empty strings from id 32310 to 32900 # Languages msgctxt "#32901" msgid "Albanian" -msgstr "" +msgstr "Albaania" msgctxt "#32902" msgid "Arabic" -msgstr "" +msgstr "Araabia" msgctxt "#32903" msgid "Belarusian" -msgstr "" +msgstr "Valgevene" msgctxt "#32904" msgid "Bosnian (Latin)" -msgstr "" +msgstr "Bosnia (ladina)" msgctxt "#32905" msgid "Bulgarian" -msgstr "" +msgstr "Bulgaaria" msgctxt "#32906" msgid "Catalan" -msgstr "" +msgstr "Katalaani" msgctxt "#32907" msgid "Chinese" -msgstr "" +msgstr "Hiina" msgctxt "#32908" msgid "Croatian" @@ -356,19 +356,19 @@ msgstr "" msgctxt "#32913" msgid "Estonian" -msgstr "" +msgstr "Eesti" msgctxt "#32914" msgid "Finnish" -msgstr "" +msgstr "Soome" msgctxt "#32915" msgid "French" -msgstr "" +msgstr "Prantsuse" msgctxt "#32916" msgid "German" -msgstr "" +msgstr "Saksa" msgctxt "#32917" msgid "Greek" @@ -426,11 +426,11 @@ msgstr "" # empty string with id 32931 msgctxt "#32932" msgid "Polish" -msgstr "" +msgstr "Poola" msgctxt "#32933" msgid "Portuguese" -msgstr "" +msgstr "Portugali" msgctxt "#32934" msgid "Portuguese (Brazil)" diff --git a/script.artistslideshow/resources/language/resource.language.fa_af/strings.po b/script.artistslideshow/resources/language/resource.language.fa_af/strings.po index 51d9a0013..0bb7b32b3 100644 --- a/script.artistslideshow/resources/language/resource.language.fa_af/strings.po +++ b/script.artistslideshow/resources/language/resource.language.fa_af/strings.po @@ -10,7 +10,7 @@ msgstr "" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" -"Language: fa_af\n" +"Language: fa_AF\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/script.artistslideshow/resources/language/resource.language.fi_fi/strings.po b/script.artistslideshow/resources/language/resource.language.fi_fi/strings.po index d5868a77c..9f8360eb5 100644 --- a/script.artistslideshow/resources/language/resource.language.fi_fi/strings.po +++ b/script.artistslideshow/resources/language/resource.language.fi_fi/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-07 18:14+0000\n" +"PO-Revision-Date: 2023-07-28 09:11+0000\n" "Last-Translator: Oskari Lavinto \n" "Language-Team: Finnish \n" "Language: fi_fi\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 4.18.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" diff --git a/script.artistslideshow/resources/language/resource.language.fr_ca/strings.po b/script.artistslideshow/resources/language/resource.language.fr_ca/strings.po index efbb96595..3186f38be 100644 --- a/script.artistslideshow/resources/language/resource.language.fr_ca/strings.po +++ b/script.artistslideshow/resources/language/resource.language.fr_ca/strings.po @@ -7,15 +7,14 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-17 10:13+0000\n" -"Last-Translator: Christian Gade \n" -"Language-Team: French (Canada) \n" -"Language: fr_ca\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fr_CA\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.11.2\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -130,7 +129,7 @@ msgstr "" msgctxt "#32119" msgid "Enable debug logging" -msgstr "Activer la journalisation de débogage" +msgstr "" msgctxt "#32120" msgid "Disable secondary and featured artist images" @@ -248,7 +247,7 @@ msgstr "" msgctxt "#32202" msgid "Error" -msgstr "Erreur" +msgstr "" msgctxt "#32203" msgid "Progress" diff --git a/script.artistslideshow/resources/language/resource.language.fr_fr/strings.po b/script.artistslideshow/resources/language/resource.language.fr_fr/strings.po index 275e71cf6..8ce09584b 100644 --- a/script.artistslideshow/resources/language/resource.language.fr_fr/strings.po +++ b/script.artistslideshow/resources/language/resource.language.fr_fr/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-10-26 17:15+0000\n" +"PO-Revision-Date: 2023-05-29 16:42+0000\n" "Last-Translator: skypichat \n" "Language-Team: French (France) \n" "Language: fr_fr\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.14.1\n" +"X-Generator: Weblate 4.17\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -52,7 +52,7 @@ msgstr "Artistes similaires" msgctxt "#32006" msgid "Advanced" -msgstr "Avancés" +msgstr "Avancé" # empty strings from id 32007 to 32099 # Settings @@ -110,11 +110,11 @@ msgstr "Emplacement personnalisé" msgctxt "#32114" msgid "Include Kodi artist fanart" -msgstr "Utiliser le diaporama prioritaire" +msgstr "Inclure le fanart de l'artiste de Kodi" msgctxt "#32115" msgid "Include Kodi album fanart" -msgstr "Inclure le fanart de l'album depuis Kodi" +msgstr "Inclure le fanart de l'album de Kodi" msgctxt "#32116" msgid "Limit size of download cache" @@ -130,7 +130,7 @@ msgstr "Nom alternatif pour le dossier local des fanart" msgctxt "#32119" msgid "Enable debug logging" -msgstr "Activer la journalisation de débogage" +msgstr "Activer la journalisation du débogage" msgctxt "#32120" msgid "Disable secondary and featured artist images" @@ -230,7 +230,7 @@ msgstr "Afficher une notification uniquement s'il y a des images à télécharge msgctxt "#32144" msgid "Use more agressive artist search for streams" -msgstr "Utilisez une recherche d'artistes plus agressive pour les flux" +msgstr "Utiliser une recherche d'artistes plus agressive pour les flux" msgctxt "#32145" msgid "Pause slideshow on playback pause" @@ -270,7 +270,7 @@ msgstr "Cette opération déplacera et renommera vos fichiers d'images et ne pou msgctxt "#32301" msgid "Unable to retrieve path to Kodi music artist image storage. Please check Settings > Media > Music and ensure the Artist Information Folder is set." -msgstr "Impossible de récupérer le chemin d'accès au stockage des images des artistes musicaux de Kodi. Veuillez vérifier Paramètres > Médiathèque > Musique et assurez-vous que le Dossier d'informations de l'artiste est défini." +msgstr "Impossible de récupérer le chemin d'accès au stockage des images des artistes musicaux de Kodi. Veuillez vérifier : Paramètres > Médiathèque > Musique et assurez-vous que le Dossier d'informations de l'artiste est défini." msgctxt "#32302" msgid "Artist Slideshow is already set to use the Kodi artist information folder. Please set to use either addon_data folder or a custom location and try again." @@ -417,7 +417,7 @@ msgstr "Lituanien" msgctxt "#32929" msgid "Macedonian" -msgstr "Macédoine" +msgstr "Macédonien" msgctxt "#32930" msgid "Norwegian" diff --git a/script.artistslideshow/resources/language/resource.language.he_il/strings.po b/script.artistslideshow/resources/language/resource.language.he_il/strings.po index 29ac6d112..a0b61bdcc 100644 --- a/script.artistslideshow/resources/language/resource.language.he_il/strings.po +++ b/script.artistslideshow/resources/language/resource.language.he_il/strings.po @@ -19,11 +19,11 @@ msgstr "" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" -msgstr "הורד תמונות ומידע נוסף על האמן המושמע כעת" +msgstr "" msgctxt "Addon Description" msgid "Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to." -msgstr "הרחבה להורדת תמונות ומידע נוסף על האמן המושמע כעת מ-fanart.tv ו- theaudiodb.com. התמונות, בנוסף לתמונות אמנים מקומיות והמידע על אמן יכולים לשמש את המעטפת ליצירת מצגת עבור האמן המושמע כעת." +msgstr "" # Section Headings msgctxt "#32000" diff --git a/script.artistslideshow/resources/language/resource.language.hr_hr/strings.po b/script.artistslideshow/resources/language/resource.language.hr_hr/strings.po index 45b51787d..0b55c266a 100644 --- a/script.artistslideshow/resources/language/resource.language.hr_hr/strings.po +++ b/script.artistslideshow/resources/language/resource.language.hr_hr/strings.po @@ -5,17 +5,17 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-01 17:13+0000\n" -"Last-Translator: Christian Gade \n" +"PO-Revision-Date: 2023-01-15 13:15+0000\n" +"Last-Translator: gogogogi \n" "Language-Team: Croatian \n" "Language: hr_hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.11\n" +"X-Generator: Weblate 4.15\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -36,15 +36,15 @@ msgstr "Pohrana" msgctxt "#32002" msgid "Images" -msgstr "" +msgstr "Slike" msgctxt "#32003" msgid "Album Info" -msgstr "" +msgstr "Informacije albuma" msgctxt "#32004" msgid "Artist Bio" -msgstr "" +msgstr "Životopis izvođača" msgctxt "#32005" msgid "Similar Artists" @@ -58,51 +58,51 @@ msgstr "Napredno" # Settings msgctxt "#32101" msgid "Download images from fanart.tv" -msgstr "" +msgstr "Preuzmi slike s fanart.tv" msgctxt "#32102" msgid "Download images from theaudiodb.com" -msgstr "" +msgstr "Preuzmi slike s theaudiodb.com" msgctxt "#32103" msgid "Download album info from the audiodb.com" -msgstr "" +msgstr "Preuzmi informacije albuma s audiodb.com" msgctxt "#32104" msgid "Download album info from the last.fm" -msgstr "" +msgstr "Preuzmi informacije albuma s last.fm" msgctxt "#32105" msgid "Download artist bio from theaudiodb.com" -msgstr "" +msgstr "Preuzmi informacije životopisa izvođača s theaudiodb.com" msgctxt "#32106" msgid "Download artist bio from last.fm" -msgstr "" +msgstr "Preuzmi informacije životopisa izvođača s last.fm" msgctxt "#32107" msgid "Download similar artists from last.fm" -msgstr "" +msgstr "Preuzmi srodne izvođače s last.fm" msgctxt "#32108" msgid "Download all images regardless of resolution" -msgstr "" +msgstr "Preuzmi sve slike neovisno o razlučivosti" msgctxt "#32109" msgid "Client API key" -msgstr "" +msgstr "API ključ klijenta" msgctxt "#32110" msgid "I donate to this service" -msgstr "" +msgstr "Donirao sam ovoj usluzi" msgctxt "#32111" msgid "Use fallback slideshow" -msgstr "" +msgstr "Koristi pričuvne slike prezentacije" msgctxt "#32112" msgid "Use override slideshow" -msgstr "" +msgstr "Koristi zaobiđene slike prezentacije" msgctxt "#32113" msgid "Custom location" @@ -110,23 +110,23 @@ msgstr "Prilagođena lokacija" msgctxt "#32114" msgid "Include Kodi artist fanart" -msgstr "" +msgstr "Uključi Kodi slike omota izvođača" msgctxt "#32115" msgid "Include Kodi album fanart" -msgstr "" +msgstr "Uključi Kodi slike omota albuma" msgctxt "#32116" msgid "Limit size of download cache" -msgstr "" +msgstr "Ograničenje veličine predmemorije preuzimanja" msgctxt "#32117" msgid "Maximum cache size (in megabytes)" -msgstr "" +msgstr "Najveća veličina predmemorije (u megabajtima)" msgctxt "#32118" msgid "Alternate name for local fanart folder" -msgstr "" +msgstr "Zamjenski naziv za lokalnu mapu slika omota" msgctxt "#32119" msgid "Enable debug logging" @@ -134,19 +134,19 @@ msgstr "Omogući zapisivanje otklanjanja grešaka" msgctxt "#32120" msgid "Disable secondary and featured artist images" -msgstr "" +msgstr "Onemogući pomoćne i istaknute slike izvođača" msgctxt "#32121" msgid "Store artist images in" -msgstr "" +msgstr "Spremi slike izvođača u" msgctxt "#32122" msgid "addon_data folder" -msgstr "" +msgstr "addon_data mapa" msgctxt "#32123" msgid "Kodi artist information folder" -msgstr "" +msgstr "Kodi mapa informacija izvođača" msgctxt "#32124" msgid "Priority" @@ -154,97 +154,97 @@ msgstr "Prioritet" msgctxt "#32125" msgid "Store artist information in" -msgstr "" +msgstr "Spremi informacije izvođača u" msgctxt "#32126" msgid "Preferred language for artist bio" -msgstr "" +msgstr "Željeni jezik životopisa izvođača" msgctxt "#32127" msgid "Use extrafanart folder" -msgstr "" +msgstr "Koristi mapu dodatnih slika omota" msgctxt "#32128" msgid "Platform for image storage" -msgstr "" +msgstr "Platforma za pohranu slika" msgctxt "#32129" msgid "Replace illegal path characters in artist name with" -msgstr "" +msgstr "Zamijeni nevaljanu putanju znakova u nazivu izvođača s" msgctxt "#32130" msgid "Replace trailing period in artist name with" -msgstr "" +msgstr "Zamijenite točku na kraju nazivu izvođača s" msgctxt "#32131" msgid "Windows" -msgstr "" +msgstr "Windows" msgctxt "#32132" msgid "Other" -msgstr "" +msgstr "Ostali" msgctxt "#32133" msgid "MacOS" -msgstr "" +msgstr "MacOS" msgctxt "#32134" msgid "Move images to Kodi music artist folder" -msgstr "" +msgstr "Premjesti slike u Kodi glazbenu mapu izvođača" msgctxt "#32135" msgid "Get artist bio from Kodi" -msgstr "" +msgstr "Nabavi životopis izvođača s Kodija" msgctxt "#32136" msgid "Custom location" -msgstr "" +msgstr "Prilagođena lokacija" msgctxt "#32137" msgid "Delay between slide transitions (in seconds)" -msgstr "" +msgstr "Odgoda između prijelaza prezentacija (u sekundama)" msgctxt "#32138" msgid "Fade to black during change in artists" -msgstr "" +msgstr "Prijeđi u crno tijekom promjene izvođača" msgctxt "#32139" msgid "Main thread sleep time (in seconds)" -msgstr "" +msgstr "Vrijeme spavanja glavnog niza (u sekundama)" msgctxt "#32140" msgid "Main thread idle sleep time (in seconds)" -msgstr "" +msgstr "Vrijeme mirovanja spavanja glavnog niza (u sekundama)" msgctxt "#32141" msgid "Slideshow thread sleep time (in seconds)" -msgstr "" +msgstr "Vrijeme mirovanja niza prezentacije (u sekundama)" msgctxt "#32142" msgid "Show notification during image downloads" -msgstr "" +msgstr "Prikaži obavijesti tijekom preuzimanja slike" msgctxt "#32143" msgid "Only show notification if there are images to download" -msgstr "" +msgstr "Samo prikaži obavijesti ako ima slika za preuzimanje" msgctxt "#32144" msgid "Use more agressive artist search for streams" -msgstr "" +msgstr "Koristi agresivniju pretragu izvođača za strujanja" msgctxt "#32145" msgid "Pause slideshow on playback pause" -msgstr "" +msgstr "Pauziraj prezentaciju pri pauzi repordukcije" # empty strings from id 32145 to 32199 # Dialog headings msgctxt "#32200" msgid "Artist Slideshow" -msgstr "" +msgstr "Prezentacija izvođača" msgctxt "#32201" msgid "Warning" -msgstr "" +msgstr "Upozorenje" msgctxt "#32202" msgid "Error" @@ -252,235 +252,235 @@ msgstr "Greška" msgctxt "#32203" msgid "Progress" -msgstr "" +msgstr "Napredak" msgctxt "#32204" msgid "Downloading" -msgstr "" +msgstr "Preuzimanje" msgctxt "#32205" msgid "Download Complete" -msgstr "" +msgstr "Preuzimanje završeno" # empty strings from id 32206 to 32299 # Dialog texts msgctxt "#32300" msgid "This will move and rename your image files and cannot be undone. Are you sure you want to do this?" -msgstr "" +msgstr "Ovo će premjestiti i preimenovati vaše datoteke slika, i ovo se ne može poništiti. Sigurno želite ovo učiniti?" msgctxt "#32301" msgid "Unable to retrieve path to Kodi music artist image storage. Please check Settings > Media > Music and ensure the Artist Information Folder is set." -msgstr "" +msgstr "Nemoguće preuzimanje putanje u Kodi glazbenu pohranu slika izvođača. Provjerite Postavke > Mediji > Glazba i pobrinite se da je mapa informacija izvođača postavljena." msgctxt "#32302" msgid "Artist Slideshow is already set to use the Kodi artist information folder. Please set to use either addon_data folder or a custom location and try again." -msgstr "" +msgstr "Prezentacija izvođača je već postavljena da koristi Kodi mapu informacija izvođača. Postavite korištenje ili addon_data mape ili prilagođenu lokaciju i pokušajte ponovno." msgctxt "#32303" msgid "Moving and renaming images from custom location..." -msgstr "" +msgstr "Premještanje i preimenovanje slika iz prilagođene lokacije..." msgctxt "#32304" msgid "Moving and renaming images from addon_data folder..." -msgstr "" +msgstr "Premještanje i preimenovanje slika iz addon_data mape..." msgctxt "#32305" msgid "No artist directories found." -msgstr "" +msgstr "Nema pronađenih direktorija izvođača." msgctxt "#32306" msgid "Image move complete. You may now change the Artist Slideshow storage location to Kodi artist information folder." -msgstr "" +msgstr "Premještanje slika je završeno. Sada možete promijeniti lokaciju pohrane prezentacije izvođača u Kodi mapu informacija izvođača." msgctxt "#32307" msgid "Downloading new artist images" -msgstr "" +msgstr "Preuzimanje novih slika izvođača" msgctxt "#32308" msgid "artist images downloaded" -msgstr "" +msgstr "slike izvođača preuzete" msgctxt "#32309" msgid "artist image downloaded" -msgstr "" +msgstr "slika izvođača preuzeta" # empty strings from id 32310 to 32900 # Languages msgctxt "#32901" msgid "Albanian" -msgstr "" +msgstr "Albanski" msgctxt "#32902" msgid "Arabic" -msgstr "" +msgstr "Arapski" msgctxt "#32903" msgid "Belarusian" -msgstr "" +msgstr "Bjeloruski" msgctxt "#32904" msgid "Bosnian (Latin)" -msgstr "" +msgstr "Bosanski (Latinica)" msgctxt "#32905" msgid "Bulgarian" -msgstr "" +msgstr "Bugarski" msgctxt "#32906" msgid "Catalan" -msgstr "" +msgstr "Katalonski" msgctxt "#32907" msgid "Chinese" -msgstr "" +msgstr "Kineski" msgctxt "#32908" msgid "Croatian" -msgstr "" +msgstr "Hrvatski" msgctxt "#32909" msgid "Czech" -msgstr "" +msgstr "Češki" msgctxt "#32910" msgid "Danish" -msgstr "" +msgstr "Danski" msgctxt "#32911" msgid "Dutch" -msgstr "" +msgstr "Nizozemski" msgctxt "#32912" msgid "English" -msgstr "" +msgstr "Engleski" msgctxt "#32913" msgid "Estonian" -msgstr "" +msgstr "Estonski" msgctxt "#32914" msgid "Finnish" -msgstr "" +msgstr "Finski" msgctxt "#32915" msgid "French" -msgstr "" +msgstr "Francuski" msgctxt "#32916" msgid "German" -msgstr "" +msgstr "Njemački" msgctxt "#32917" msgid "Greek" -msgstr "" +msgstr "Grčki" msgctxt "#32918" msgid "Hebrew" -msgstr "" +msgstr "Hebrejski" msgctxt "#32919" msgid "Hindi" -msgstr "" +msgstr "Hindski" msgctxt "#32920" msgid "Hungarian" -msgstr "" +msgstr "Mađarski" msgctxt "#32921" msgid "Icelandic" -msgstr "" +msgstr "Islandski" msgctxt "#32922" msgid "Indonesian" -msgstr "" +msgstr "Indonezijski" # empty string with id 32923 msgctxt "#32924" msgid "Italian" -msgstr "" +msgstr "Talijanski" msgctxt "#32925" msgid "Japanese" -msgstr "" +msgstr "Japanski" msgctxt "#32926" msgid "Korean" -msgstr "" +msgstr "Korejski" msgctxt "#32927" msgid "Latvian" -msgstr "" +msgstr "Latvijski" msgctxt "#32928" msgid "Lithuanian" -msgstr "" +msgstr "Letonski" msgctxt "#32929" msgid "Macedonian" -msgstr "" +msgstr "Makedonski" msgctxt "#32930" msgid "Norwegian" -msgstr "" +msgstr "Norveški" # empty string with id 32931 msgctxt "#32932" msgid "Polish" -msgstr "" +msgstr "Poljski" msgctxt "#32933" msgid "Portuguese" -msgstr "" +msgstr "Portugalski" msgctxt "#32934" msgid "Portuguese (Brazil)" -msgstr "" +msgstr "Portugalski (Brazil)" msgctxt "#32935" msgid "Romanian" -msgstr "" +msgstr "Rumunjski" msgctxt "#32936" msgid "Russian" -msgstr "" +msgstr "Ruski" msgctxt "#32937" msgid "SerbianLatin" -msgstr "" +msgstr "Srpski latinica" msgctxt "#32938" msgid "Slovak" -msgstr "" +msgstr "Slovački" msgctxt "#32939" msgid "Slovenian" -msgstr "" +msgstr "Slovenski" msgctxt "#32940" msgid "Spanish" -msgstr "" +msgstr "Španjolski" # empty string with id 32941 msgctxt "#32942" msgid "Swedish" -msgstr "" +msgstr "Švedski" msgctxt "#32943" msgid "Thai" -msgstr "" +msgstr "Tajlandski" msgctxt "#32944" msgid "Turkish" -msgstr "" +msgstr "Turski" msgctxt "#32945" msgid "Ukrainian" -msgstr "" +msgstr "Ukrajinski" msgctxt "#32946" msgid "Vietnamese" -msgstr "" +msgstr "Vijetnamski" msgctxt "#32947" msgid "Farsi" -msgstr "" +msgstr "Farsi" diff --git a/script.artistslideshow/resources/language/resource.language.it_it/strings.po b/script.artistslideshow/resources/language/resource.language.it_it/strings.po index d3711106e..bff0cc280 100644 --- a/script.artistslideshow/resources/language/resource.language.it_it/strings.po +++ b/script.artistslideshow/resources/language/resource.language.it_it/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-08-23 07:14+0000\n" +"PO-Revision-Date: 2023-05-22 04:42+0000\n" "Last-Translator: Massimo Pissarello \n" "Language-Team: Italian \n" "Language: it_it\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 4.17\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -23,12 +23,12 @@ msgstr "Scarica immagini e informazioni aggiuntive dell'artista attualmente in r msgctxt "Addon Description" msgid "Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to." -msgstr "Addon per scaricare immagini ed informazioni aggiuntive da fanart.tv e theaudiodb.com sull'artista attualmente in esecuzione. Le immagini possono essere usate dallo skin, insieme a quelle locali, per creare una presentazione per l'artista che si sta ascoltando." +msgstr "Add-on per scaricare immagini ed informazioni aggiuntive da fanart.tv e theaudiodb.com sull'artista attualmente in riproduzione. Le immagini possono essere usate dalla skin, insieme a quelle locali, per creare una presentazione per l'artista che si sta ascoltando." # Section Headings msgctxt "#32000" msgid "Slideshow" -msgstr "Slideshow" +msgstr "Presentazione" msgctxt "#32001" msgid "Storage" @@ -90,7 +90,7 @@ msgstr "Scarica tutte le immagini indipendentemente dalla risoluzione" msgctxt "#32109" msgid "Client API key" -msgstr "Chiave API client" +msgstr "Chiave API del client" msgctxt "#32110" msgid "I donate to this service" @@ -102,7 +102,7 @@ msgstr "Usa presentazione di riserva" msgctxt "#32112" msgid "Use override slideshow" -msgstr "Usa override presentazione" +msgstr "Usa sovrascrivi presentazione" msgctxt "#32113" msgid "Custom location" @@ -110,15 +110,15 @@ msgstr "Posizione personalizzata" msgctxt "#32114" msgid "Include Kodi artist fanart" -msgstr "Includi fanart dell'artista Kodi" +msgstr "Includi fanart dell'artista di Kodi" msgctxt "#32115" msgid "Include Kodi album fanart" -msgstr "Includi fanart dell'album Kodi" +msgstr "Includi fanart dell'album di Kodi" msgctxt "#32116" msgid "Limit size of download cache" -msgstr "Limita la dimensione della cache di download" +msgstr "Limita dimensione della cache di download" msgctxt "#32117" msgid "Maximum cache size (in megabytes)" @@ -134,11 +134,11 @@ msgstr "Registra eventi di debug" msgctxt "#32120" msgid "Disable secondary and featured artist images" -msgstr "Disabilita immagini secondarie e in primo piano dell'artista" +msgstr "Disabilita immagini degli artisti secondari e in evidenza" msgctxt "#32121" msgid "Store artist images in" -msgstr "Memorizza le immagini dell'artista in" +msgstr "Memorizza immagini dell'artista in" msgctxt "#32122" msgid "addon_data folder" @@ -154,11 +154,11 @@ msgstr "Priorità" msgctxt "#32125" msgid "Store artist information in" -msgstr "Memorizza le informazioni sull'artista in" +msgstr "Memorizza informazioni sull'artista in" msgctxt "#32126" msgid "Preferred language for artist bio" -msgstr "Linguaggio preferito per la biografia dell'artista" +msgstr "Lingua preferita per la biografia dell'artista" msgctxt "#32127" msgid "Use extrafanart folder" @@ -170,7 +170,7 @@ msgstr "Piattaforma per archiviazione immagini" msgctxt "#32129" msgid "Replace illegal path characters in artist name with" -msgstr "Sostituisci i caratteri di percorso illegali nel nome dell'artista con" +msgstr "Sostituisci i caratteri non validi nel percorso del nome dell'artista con" msgctxt "#32130" msgid "Replace trailing period in artist name with" @@ -190,11 +190,11 @@ msgstr "MacOS" msgctxt "#32134" msgid "Move images to Kodi music artist folder" -msgstr "Sposta le immagini nella cartella dell'artista musicale di Kodi" +msgstr "Sposta immagini nella cartella dell'artista musicale di Kodi" msgctxt "#32135" msgid "Get artist bio from Kodi" -msgstr "Ottieni la biografia dell'artista da Kodi" +msgstr "Ottieni biografia dell'artista da Kodi" msgctxt "#32136" msgid "Custom location" @@ -206,41 +206,41 @@ msgstr "Ritardo tra le transizioni delle diapositive (in secondi)" msgctxt "#32138" msgid "Fade to black during change in artists" -msgstr "Sfuma al nero durante il cambio degli artisti" +msgstr "Sfuma sul nero durante il cambio artisti" msgctxt "#32139" msgid "Main thread sleep time (in seconds)" -msgstr "Tempo di sospensione del thread principale (in secondi)" +msgstr "Tempo di attesa del thread principale (sec)" msgctxt "#32140" msgid "Main thread idle sleep time (in seconds)" -msgstr "Tempo di inattività del thread principale (in secondi)" +msgstr "Tempo di inattività del thread principale (sec)" msgctxt "#32141" msgid "Slideshow thread sleep time (in seconds)" -msgstr "Tempo di sospensione del thread della presentazione (in secondi)" +msgstr "Tempo di attesa del thread della presentazione (sec)" msgctxt "#32142" msgid "Show notification during image downloads" -msgstr "Mostra la notifica durante il download delle immagini" +msgstr "Mostra notifica durante il download delle immagini" msgctxt "#32143" msgid "Only show notification if there are images to download" -msgstr "Mostra la notifica solo se ci sono immagini da scaricare" +msgstr "Mostra notifica solo se ci sono immagini da scaricare" msgctxt "#32144" msgid "Use more agressive artist search for streams" -msgstr "Usa una ricerca artistica più aggressiva per gli stream" +msgstr "Usa una ricerca di artisti più aggressiva per i flussi" msgctxt "#32145" msgid "Pause slideshow on playback pause" -msgstr "Metti in pausa la presentazione durante la pausa di riproduzione" +msgstr "Metti in pausa la presentazione quando la riproduzione è in pausa" # empty strings from id 32145 to 32199 # Dialog headings msgctxt "#32200" msgid "Artist Slideshow" -msgstr "Presentazione dell'artista" +msgstr "Presentazione artista" msgctxt "#32201" msgid "Warning" @@ -286,7 +286,7 @@ msgstr "Spostamento e ridenominazione di immagini dalla cartella addon_data..." msgctxt "#32305" msgid "No artist directories found." -msgstr "Nessuna directory artisti trovata." +msgstr "Nessuna cartella artisti trovata." msgctxt "#32306" msgid "Image move complete. You may now change the Artist Slideshow storage location to Kodi artist information folder." diff --git a/script.artistslideshow/resources/language/resource.language.pl_pl/strings.po b/script.artistslideshow/resources/language/resource.language.pl_pl/strings.po index adc28cdd3..29a5907f2 100644 --- a/script.artistslideshow/resources/language/resource.language.pl_pl/strings.po +++ b/script.artistslideshow/resources/language/resource.language.pl_pl/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-30 09:46+0000\n" +"PO-Revision-Date: 2023-12-12 14:11+0000\n" "Last-Translator: Christian Gade \n" "Language-Team: Polish \n" "Language: pl_pl\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.11.2\n" +"X-Generator: Weblate 5.2.1\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -36,7 +36,7 @@ msgstr "" msgctxt "#32002" msgid "Images" -msgstr "" +msgstr "Obrazy" msgctxt "#32003" msgid "Album Info" diff --git a/script.artistslideshow/resources/language/resource.language.pt_pt/strings.po b/script.artistslideshow/resources/language/resource.language.pt_pt/strings.po index 20952974e..dc8b71135 100644 --- a/script.artistslideshow/resources/language/resource.language.pt_pt/strings.po +++ b/script.artistslideshow/resources/language/resource.language.pt_pt/strings.po @@ -19,11 +19,11 @@ msgstr "" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" -msgstr "Transferir imagens e info adicional do artista em reprodução agora" +msgstr "" msgctxt "Addon Description" msgid "Addon to download images and additional information from fanart.tv and theaudiodb.com of the currently playing artist. The images, along with local artists' images, and info can be used by the skin to create a slideshow for the artist being listened to." -msgstr "Um add-on para transferir imagens e informação adicional do artista em reprodução no momento, a partir de fanart.tv e theaudiodb.com. As imagens e a informação, assim como as imagens locais dos artistas, podem ser usadas pelo tema para criar uma apresentação de slides do artista em escuta no momento." +msgstr "" # Section Headings msgctxt "#32000" diff --git a/script.artistslideshow/resources/language/resource.language.ru_ru/strings.po b/script.artistslideshow/resources/language/resource.language.ru_ru/strings.po index a0d5e177e..adbb9f4ef 100644 --- a/script.artistslideshow/resources/language/resource.language.ru_ru/strings.po +++ b/script.artistslideshow/resources/language/resource.language.ru_ru/strings.po @@ -5,17 +5,17 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-11-09 20:21+0000\n" -"Last-Translator: vgbsd \n" +"PO-Revision-Date: 2024-02-29 09:13+0000\n" +"Last-Translator: Alexey \n" "Language-Team: Russian \n" "Language: ru_ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.14.2\n" +"X-Generator: Weblate 5.4\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -102,7 +102,7 @@ msgstr "Использовать резервное слайд-шоу" msgctxt "#32112" msgid "Use override slideshow" -msgstr "" +msgstr "Использовать переопределение слайд-шоу" msgctxt "#32113" msgid "Custom location" @@ -110,11 +110,11 @@ msgstr "Собственное расположение" msgctxt "#32114" msgid "Include Kodi artist fanart" -msgstr "Включить иллюстрации художников Kodi" +msgstr "Использовать иллюстрации исполнителей из Kodi" msgctxt "#32115" msgid "Include Kodi album fanart" -msgstr "Включить обложки альбомов Kodi" +msgstr "Использовать обложки альбомов из Kodi" msgctxt "#32116" msgid "Limit size of download cache" @@ -170,11 +170,11 @@ msgstr "Платформа хранения изображений" msgctxt "#32129" msgid "Replace illegal path characters in artist name with" -msgstr "Замените недопустимые символы пути в имени исполнителя" +msgstr "Заменить недопустимые символы пути в имени исполнителя на" msgctxt "#32130" msgid "Replace trailing period in artist name with" -msgstr "" +msgstr "Заменить точку в имени исполнителя на" msgctxt "#32131" msgid "Windows" @@ -218,7 +218,7 @@ msgstr "Время ожидания бездействия основного п msgctxt "#32141" msgid "Slideshow thread sleep time (in seconds)" -msgstr "" +msgstr "Время задержки в режиме слайд-шоу (в секундах)" msgctxt "#32142" msgid "Show notification during image downloads" @@ -234,7 +234,7 @@ msgstr "Использовать более интенсивный поиск и msgctxt "#32145" msgid "Pause slideshow on playback pause" -msgstr "" +msgstr "Приостановка слайд-шоу на паузу при воспроизведении" # empty strings from id 32145 to 32199 # Dialog headings @@ -244,7 +244,7 @@ msgstr "Слайд-шоу исполнителей" msgctxt "#32201" msgid "Warning" -msgstr "" +msgstr "Внимание" msgctxt "#32202" msgid "Error" @@ -252,7 +252,7 @@ msgstr "Ошибка" msgctxt "#32203" msgid "Progress" -msgstr "" +msgstr "Прогресс" msgctxt "#32204" msgid "Downloading" @@ -274,7 +274,7 @@ msgstr "Не удаётся установить путь к каталогу с msgctxt "#32302" msgid "Artist Slideshow is already set to use the Kodi artist information folder. Please set to use either addon_data folder or a custom location and try again." -msgstr "" +msgstr "Artist Slideshow уже настроено на использование папки с информацией об исполнителе в Kodi. Пожалуйста, задайте использование папки addon_data или пользовательского расположения и повторите попытку." msgctxt "#32303" msgid "Moving and renaming images from custom location..." @@ -282,7 +282,7 @@ msgstr "Перемещение и переименование изображе msgctxt "#32304" msgid "Moving and renaming images from addon_data folder..." -msgstr "" +msgstr "Перемещение и переименование изображений из папки addon_data..." msgctxt "#32305" msgid "No artist directories found." diff --git a/script.artistslideshow/resources/language/resource.language.sk_sk/strings.po b/script.artistslideshow/resources/language/resource.language.sk_sk/strings.po index 276096272..488d5833c 100644 --- a/script.artistslideshow/resources/language/resource.language.sk_sk/strings.po +++ b/script.artistslideshow/resources/language/resource.language.sk_sk/strings.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-04-08 00:13+0000\n" +"PO-Revision-Date: 2023-10-27 12:11+0000\n" "Last-Translator: Christian Gade \n" "Language-Team: Slovak \n" "Language: sk_sk\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 4.11.2\n" +"X-Generator: Weblate 5.1\n" msgctxt "Addon Summary" msgid "Download images and additional info of the currently playing artist" @@ -28,11 +28,11 @@ msgstr "" # Section Headings msgctxt "#32000" msgid "Slideshow" -msgstr "" +msgstr "Prezentácia" msgctxt "#32001" msgid "Storage" -msgstr "" +msgstr "Úložisko" msgctxt "#32002" msgid "Images" @@ -52,7 +52,7 @@ msgstr "" msgctxt "#32006" msgid "Advanced" -msgstr "" +msgstr "Pokročilé" # empty strings from id 32007 to 32099 # Settings diff --git a/script.artistslideshow/resources/lib/artistslideshow.py b/script.artistslideshow/resources/lib/artistslideshow.py index c929f3619..1d31d6950 100644 --- a/script.artistslideshow/resources/lib/artistslideshow.py +++ b/script.artistslideshow/resources/lib/artistslideshow.py @@ -25,8 +25,9 @@ import threading import time import json as _json -from kodi_six import xbmc, xbmcgui, xbmcvfs -from kodi_six.utils import py2_encode, py2_decode +import xbmc +import xbmcgui +import xbmcvfs from resources.plugins import * from resources.lib.fileops import * from resources.lib.url import URL @@ -115,7 +116,7 @@ def AddImage(self, path): if not path: LW.log(['Image path was empty, nothing added']) return False - if path.endswith(self.VALIDIMAGETYPES): + if path.lower().endswith(self.VALIDIMAGETYPES): self.IMAGES.append(path) LW.log(['Added to image display group: ' + path]) self.IMAGEADDED = True @@ -293,8 +294,7 @@ def _clean_dir(self, dir_path): LW.log(['unexpected error while getting directory list', e]) return for old_file in old_files: - success, loglines = deleteFile( - os.path.join(dir_path, py2_encode(old_file))) + success, loglines = deleteFile(os.path.join(dir_path, old_file)) LW.log(loglines) def _clean_text(self, text): @@ -438,7 +438,7 @@ def _get_artistbio(self): bio_params['mbid'] = self.MBID bio_params['infodir'] = self.INFODIR bio_params['localartistdir'] = os.path.join( - self.LOCALARTISTPATH, py2_decode(self.NAME)) + self.LOCALARTISTPATH, self.NAME) bio_params['lang'] = self.LANGUAGE bio_params['artist'] = self.NAME bio = '' @@ -464,7 +464,7 @@ def _get_artistalbums(self): album_params = {} album_params['infodir'] = self.INFODIR album_params['localartistdir'] = os.path.join( - self.LOCALARTISTPATH, py2_decode(self.NAME)) + self.LOCALARTISTPATH, self.NAME) album_params['lang'] = self.LANGUAGE album_params['artist'] = self.NAME albums = [] @@ -491,7 +491,7 @@ def _get_artistsimilar(self): similar_params = {} similar_params['infodir'] = self.INFODIR similar_params['localartistdir'] = os.path.join( - self.LOCALARTISTPATH, py2_decode(self.NAME)) + self.LOCALARTISTPATH, self.NAME) similar_params['lang'] = self.LANGUAGE similar_params['artist'] = self.NAME similar_artists = [] @@ -562,8 +562,8 @@ def _get_current_artists_filtered(self, artist_names, mbids): LW.log(['left with', artist_names]) for artist_name, mbid in _zip_longest(artist_names, mbids, fillvalue=''): if artist_name: - artists_info.append((py2_encode(artist_name), self._get_musicbrainz_id( - py2_encode(artist_name), mbid))) + artists_info.append( + (artist_name, self._get_musicbrainz_id(artist_name, mbid))) return artists_info def _get_current_artists_info(self): @@ -961,11 +961,10 @@ def _move_to_kodi_storage(self): for thedir in dirs: if (src == self.LOCALARTISTPATH) and self.USEFANARTFOLDER: image_src = os.path.join( - self.LOCALARTISTPATH, py2_decode(thedir), self.FANARTFOLDER) + self.LOCALARTISTPATH, thedir, self.FANARTFOLDER) else: - image_src = os.path.join(src, py2_decode(thedir)) - image_dest = os.path.join( - kodi_music_artist_path, py2_decode(thedir)) + image_src = os.path.join(src, thedir) + image_dest = os.path.join(kodi_music_artist_path, thedir) LW.log(['moving images from %s to %s' % (image_src, image_dest)]) files = self._get_file_list(image_src) self.FANARTNUMBER = False @@ -1050,7 +1049,7 @@ def _remove_trailing_dot(self, thename): def _set_artwork_from_dir(self, thedir, files): for thefile in files: - self.SLIDESHOW.AddImage(os.path.join(thedir, py2_decode(thefile))) + self.SLIDESHOW.AddImage(os.path.join(thedir, thefile)) def _set_cachedir(self, theartist): self.CACHEDIR = self._set_thedir(theartist, 'ArtistSlideshow') @@ -1122,10 +1121,6 @@ def _set_safe_artist_name(self, theartist): s_name = s_name + self.ILLEGALREPLACE else: s_name = s_name + c - try: - s_name = py2_decode(s_name) - except UnicodeDecodeError: - s_name = '' return s_name def _set_thedir(self, theartist, dirtype): @@ -1158,14 +1153,13 @@ def _trim_cache(self): if (now - self.LASTCACHETRIM > cache_trim_delay): LW.log(['trimming the cache down to %s bytes' % self.MAXCACHESIZE]) - cache_root = py2_encode(os.path.join( - ADDONDATAPATH, 'ArtistSlideshow', '')) + cache_root = os.path.join(ADDONDATAPATH, 'ArtistSlideshow', '') folders, fls = xbmcvfs.listdir(cache_root) LW.log(['cache folders returned:']) LW.log(folders) try: folders.sort(key=lambda x: os.path.getmtime( - os.path.join(cache_root, py2_encode(x))), reverse=True) + os.path.join(cache_root, x)), reverse=True) except Exception as e: LW.log(['unexpected error sorting cache directory', e]) return @@ -1175,19 +1169,17 @@ def _trim_cache(self): if self._playback_stopped_or_changed(wait_time=0.1): break cache_size = cache_size + \ - self._get_folder_size(os.path.join( - cache_root, py2_encode(folder))) + self._get_folder_size(os.path.join(cache_root, folder)) LW.log(['looking at folder %s cache size is now %s' % (folder, cache_size)]) if (cache_size > self.MAXCACHESIZE and not first_folder): - self._trim_one_folder(os.path.join( - cache_root, py2_encode(folder))) + self._trim_one_folder(os.path.join(cache_root, folder)) if self.LOCALINFOSTORAGE and self.LOCALINFOPATH: - self._trim_one_folder(os.path.join(self.LOCALINFOPATH, py2_decode( - folder))) + self._trim_one_folder( + os.path.join(self.LOCALINFOPATH, folder)) else: self._trim_one_folder(os.path.join( - ADDONDATAPATH, 'ArtistInformation', py2_decode(folder))) + ADDONDATAPATH, 'ArtistInformation', folder)) first_folder = False self.LASTCACHETRIM = now @@ -1231,8 +1223,8 @@ def _use_correct_artwork(self): self.ARTISTNUM += 1 self.NAME = artist self.MBID = mbid - self._set_infodir(py2_decode(self.NAME)) - self._set_cachedir(py2_decode(self.NAME)) + self._set_infodir(self.NAME) + self._set_cachedir(self.NAME) if (self.ARTISTNUM == 1): self._get_artistinfo() images = self._get_file_list(self.CACHEDIR, do_filter=True) @@ -1300,10 +1292,8 @@ def _upgrade(self): dirs = [] if dirs: for thedir in dirs: - src = os.path.join( - src_root, py2_decode(thedir), self.IMGDB) - dst = os.path.join( - dst_root, py2_decode(thedir), self.IMGDB) + src = os.path.join(src_root, thedir, self.IMGDB) + dst = os.path.join(dst_root, thedir, self.IMGDB) success, loglines = moveFile(src, dst) LW.log(loglines) src_root = getSettingString('local_artist_path') @@ -1319,10 +1309,10 @@ def _upgrade(self): dirs = [] if dirs: for thedir in dirs: - src = os.path.join(src_root, py2_decode( - thedir), self.FANARTFOLDER, self.IMGDB) - dst = os.path.join(dst_root, py2_decode( - thedir), 'information', self.IMGDB) + src = os.path.join( + src_root, thedir, self.FANARTFOLDER, self.IMGDB) + dst = os.path.join( + dst_root, thedir, 'information', self.IMGDB) success, loglines = moveFile(src, dst) LW.log(loglines) self._update_check_file( diff --git a/script.artistslideshow/resources/lib/fileops.py b/script.artistslideshow/resources/lib/fileops.py index 7c5803ed7..311ea66d7 100644 --- a/script.artistslideshow/resources/lib/fileops.py +++ b/script.artistslideshow/resources/lib/fileops.py @@ -4,7 +4,7 @@ import re import sys try: - from kodi_six import xbmcvfs + import xbmcvfs isXBMC = True except ImportError: isXBMC = False diff --git a/script.artistslideshow/resources/lib/xlogger.py b/script.artistslideshow/resources/lib/xlogger.py index 67aa3d7ba..6e21ced8e 100644 --- a/script.artistslideshow/resources/lib/xlogger.py +++ b/script.artistslideshow/resources/lib/xlogger.py @@ -1,7 +1,7 @@ # v.0.4.14 try: - from kodi_six import xbmc + import xbmc LOGTYPE = 'xbmc' except ImportError: import os diff --git a/script.artistslideshow/resources/plugins/fanarttv.py b/script.artistslideshow/resources/plugins/fanarttv.py index 9b2c31188..2aa7a50b7 100644 --- a/script.artistslideshow/resources/plugins/fanarttv.py +++ b/script.artistslideshow/resources/plugins/fanarttv.py @@ -4,10 +4,9 @@ import os import time import random -from kodi_six import xbmcvfs +import xbmcvfs from resources.lib.url import URL from resources.lib.fileops import readFile, writeFile, deleteFile, checkPath -from kodi_six.utils import py2_encode import json as _json try: from . import fanarttv_info as settings @@ -102,7 +101,7 @@ def _get_data(self, filepath, cachefilepath, url, url_params): self.LOGLINES.extend(uloglines) if success: success, wloglines = writeFile( - py2_encode(_json.dumps(json_data)), filepath) + _json.dumps(json_data), filepath) self.LOGLINES.extend(wloglines) exists, cloglines = checkPath(filepath, False) self.LOGLINES.extend(cloglines) diff --git a/script.artistslideshow/resources/plugins/kodi.py b/script.artistslideshow/resources/plugins/kodi.py index 9f4dfc9ec..2ef28f858 100644 --- a/script.artistslideshow/resources/plugins/kodi.py +++ b/script.artistslideshow/resources/plugins/kodi.py @@ -1,6 +1,6 @@ # v.0.3.0 -from kodi_six import xbmc +import xbmc import json as _json diff --git a/script.artistslideshow/resources/plugins/lastfm.py b/script.artistslideshow/resources/plugins/lastfm.py index 5c361c590..480ccd504 100644 --- a/script.artistslideshow/resources/plugins/lastfm.py +++ b/script.artistslideshow/resources/plugins/lastfm.py @@ -7,8 +7,7 @@ import defusedxml.ElementTree as _xmltree from resources.lib.url import URL from resources.lib.fileops import readFile, writeFile, checkPath -from kodi_six.utils import py2_encode -from kodi_six import xbmcvfs +import xbmcvfs try: from . import lastfm_info as settings except ImportError: @@ -55,8 +54,8 @@ def getAlbumList(self, album_params): list(additionalparams.items())) self.LOGLINES.append('trying to get artist albums from ' + self.URL) try: - xmldata = _xmltree.fromstring(py2_encode( - self._get_data(filepath, cachefilepath, url_params))) + xmldata = _xmltree.fromstring(self._get_data( + filepath, cachefilepath, url_params)) except _xmltree.ParseError: self.LOGLINES.append('error reading XML file') return [], self.LOGLINES @@ -66,7 +65,7 @@ def getAlbumList(self, album_params): if match: match = False else: - name = py2_encode(element.text) + name = element.text match = True elif element.tag == "image": if element.attrib.get('size') == "extralarge": @@ -95,8 +94,8 @@ def getBio(self, bio_params): list(additionalparams.items())) self.LOGLINES.append('trying to get artist bio from ' + self.URL) try: - xmldata = _xmltree.fromstring(py2_encode( - self._get_data(filepath, cachefilepath, url_params))) + xmldata = _xmltree.fromstring( + self._get_data(filepath, cachefilepath, url_params)) except _xmltree.ParseError: self.LOGLINES.append('error reading XML file') return '', self.LOGLINES @@ -122,8 +121,8 @@ def getSimilarArtists(self, sim_params): list(additionalparams.items())) self.LOGLINES.append('trying to get similar artists from ' + self.URL) try: - xmldata = _xmltree.fromstring(py2_encode( - self._get_data(filepath, cachefilepath, url_params))) + xmldata = _xmltree.fromstring( + self._get_data(filepath, cachefilepath, url_params)) except _xmltree.ParseError: self.LOGLINES.append('error reading XML file') return [], self.LOGLINES @@ -133,7 +132,7 @@ def getSimilarArtists(self, sim_params): if match: match = False else: - name = py2_encode(element.text) + name = element.text match = True elif element.tag == "image": if element.attrib.get('size') == "extralarge": @@ -159,7 +158,7 @@ def getMBID(self, mbid_params): rloglines, rawxml = readFile(filepath) self.LOGLINES.extend(rloglines) try: - xmldata = _xmltree.fromstring(py2_encode(rawxml)) + xmldata = _xmltree.fromstring(rawxml) except _xmltree.ParseError: self.LOGLINES.append( 'error reading musicbrainz ID from ' + filepath) @@ -198,7 +197,7 @@ def _get_data(self, filepath, cachefilepath, url_params): self.URL, params=url_params) self.LOGLINES.extend(uloglines) if success: - success, wloglines = writeFile(py2_encode(data), filepath) + success, wloglines = writeFile(data, filepath) self.LOGLINES.extend(wloglines) exists, cloglines = checkPath(filepath, False) self.LOGLINES.extend(cloglines) diff --git a/script.artistslideshow/resources/plugins/local.py b/script.artistslideshow/resources/plugins/local.py index 14e7dc7a9..10a1e5bea 100644 --- a/script.artistslideshow/resources/plugins/local.py +++ b/script.artistslideshow/resources/plugins/local.py @@ -3,7 +3,6 @@ import os import defusedxml.ElementTree as _xmltree from resources.lib.fileops import readFile, checkPath -from kodi_six.utils import py2_encode, py2_decode class objectConfig(object): @@ -21,18 +20,18 @@ def getAlbumList(self, album_params): albums = [] filepath = os.path.join(album_params.get( 'localartistdir', ''), self.ALBUMFILEPATH) - local_path = os.path.join(album_params.get('localartistdir', ''), py2_decode( - album_params.get('artist', '')), 'override') + local_path = os.path.join(album_params.get( + 'localartistdir', ''), album_params.get('artist', ''), 'override') self.loglines.append('checking ' + filepath) rloglines, rawxml = readFile(filepath) self.loglines.extend(rloglines) if rawxml: - xmldata = _xmltree.fromstring(py2_encode(rawxml)) + xmldata = _xmltree.fromstring(rawxml) else: return [], self.loglines for element in xmldata.iter(): if element.tag == "name": - name = py2_encode(element.text) + name = element.text elif element.tag == "image": image_text = element.text if not image_text: @@ -55,7 +54,7 @@ def getBio(self, bio_params): loglines, rawxml = readFile(filepath) self.loglines.extend(loglines) if rawxml: - xmldata = _xmltree.fromstring(py2_encode(rawxml)) + xmldata = _xmltree.fromstring(rawxml) else: return '', self.loglines for element in xmldata.iter(): @@ -85,18 +84,18 @@ def getSimilarArtists(self, sim_params): similar_artists = [] filepath = os.path.join(sim_params.get( 'localartistdir', ''), self.SIMILARFILEPATH) - local_path = os.path.join(sim_params.get('localartistdir', ''), py2_decode( - sim_params.get('artist', '')), 'override') + local_path = os.path.join(sim_params.get('localartistdir', ''), + sim_params.get('artist', ''), 'override') self.loglines.append('checking ' + filepath) rloglines, rawxml = readFile(filepath) self.loglines.extend(rloglines) if rawxml: - xmldata = _xmltree.fromstring(py2_encode(rawxml)) + xmldata = _xmltree.fromstring(rawxml) else: return [], self.loglines for element in xmldata.iter(): if element.tag == "name": - name = py2_encode(element.text) + name = element.text elif element.tag == "image": image_text = element.text if not image_text: diff --git a/script.artistslideshow/resources/plugins/theaudiodb.py b/script.artistslideshow/resources/plugins/theaudiodb.py index a3ae4c2bd..54c37ed08 100644 --- a/script.artistslideshow/resources/plugins/theaudiodb.py +++ b/script.artistslideshow/resources/plugins/theaudiodb.py @@ -4,10 +4,9 @@ import os import time import random -from kodi_six import xbmcvfs +import xbmcvfs from resources.lib.url import URL from resources.lib.fileops import readFile, writeFile, deleteFile, checkPath -from kodi_six.utils import py2_encode import json as _json try: from . import theaudiodb_info as settings @@ -100,7 +99,7 @@ def getImageList(self, img_params): if json_data: artist = json_data.get('artists') if artist is not None: - for count in range(3): + for count in range(4): if count == 0: num = '' else: @@ -225,7 +224,7 @@ def _get_data(self, filepath, cachefilepath, url, url_params): self.LOGLINES.extend(uloglines) if success: success, wloglines = writeFile( - py2_encode(_json.dumps(json_data)), filepath) + _json.dumps(json_data), filepath) self.LOGLINES.extend(wloglines) exists, cloglines = checkPath(filepath, False) self.LOGLINES.extend(cloglines) diff --git a/script.artistslideshow/resources/settings.xml b/script.artistslideshow/resources/settings.xml index 61001d315..e122b08a9 100644 --- a/script.artistslideshow/resources/settings.xml +++ b/script.artistslideshow/resources/settings.xml @@ -543,6 +543,9 @@ 0 _ + + true + 32129 diff --git a/script.audio.motherearth/CHANGELOG.md b/script.audio.motherearth/CHANGELOG.md index d4784f1ce..10d08a4ad 100644 --- a/script.audio.motherearth/CHANGELOG.md +++ b/script.audio.motherearth/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.2 +- added Mother Earth Jazz channel + +## 2.1 +- updated to https +- added artist fanart background when available + ## v2.0 - metadata for the streams diff --git a/script.audio.motherearth/README.md b/script.audio.motherearth/README.md index ddb35d6bc..8341a88a0 100644 --- a/script.audio.motherearth/README.md +++ b/script.audio.motherearth/README.md @@ -1,6 +1,6 @@ # script.audio.motherearth -Kodi addon for Mother Earth Radio, a free FLAC/AAC 96k/24bits webradio +Mother Earth Radio - Hi Res Audio and Vinyl 192kHz/24bit FLAC streaming -Version 2.0 +Version 2.2 -Choose FLAC or AAC-stream (AAC is the default) in addon options. +Choose FLAC or AAC-stream in addon options. diff --git a/script.audio.motherearth/addon.xml b/script.audio.motherearth/addon.xml index 41aa89a2e..3cc0686be 100644 --- a/script.audio.motherearth/addon.xml +++ b/script.audio.motherearth/addon.xml @@ -1,5 +1,5 @@ - + @@ -9,15 +9,14 @@ - Mother Earth Radio addon for Kodi - Free High Resolution FLAC 96kHz/24bit Radio. 429hz music tuned to the Faslak's Frequency. Choose FLAC or AAC stream in addon-options + Mother Earth Radio - 192kHz/24bit streaming + Hi Res Audio and Vinyl- Mother Earth is a free HD music station without advertising. Best audio quality, selected tracks, audiophile only. Stream now! https://motherearthradio.de/ https://github.com/FlorianReiterer/repo-scripts/tree/script.audio.motherearth GPL-3.0-or-later all -- added Classical and Instrumental stream -- Metadata for the streams + added Mother Earth Jazz channel resources/icon.png diff --git a/script.audio.motherearth/motherearth.py b/script.audio.motherearth/motherearth.py index c61b7e371..a1ddd4f1c 100644 --- a/script.audio.motherearth/motherearth.py +++ b/script.audio.motherearth/motherearth.py @@ -4,26 +4,32 @@ KEY_FILTER_RE = re.compile(r'[^\w\']+') -NOWPLAYING_URL = 'http://server9.streamserver24.com:9090/api/nowplaying/{}' +NOWPLAYING_URL = 'https://motherearth.streamserver24.com/api/nowplaying/{}' STREAMS = [ { 'channel': 0, 'title': 'Mother Earth Radio', - 'url_aac': 'http://server9.streamserver24.com:18900/motherearth.aac', - 'url_flac': 'http://server9.streamserver24.com:18900/motherearth', + 'url_aac': 'https://motherearth.streamserver24.com/listen/motherearth/motherearth.aac', + 'url_flac': 'https://motherearth.streamserver24.com/listen/motherearth/motherearth', }, { 'channel': 1, 'title': 'Mother Earth Klassik', - 'url_aac': 'http://server9.streamserver24.com:18910/motherearth.klassik.aac', - 'url_flac': 'http://server9.streamserver24.com:18910/motherearth.klassik', + 'url_aac': 'https://motherearth.streamserver24.com/listen/motherearth_klassik/motherearth.klassik.aac', + 'url_flac': 'https://motherearth.streamserver24.com/listen/motherearth_klassik/motherearth.klassik', }, { 'channel': 2, 'title': 'Mother Earth Instrumental', - 'url_aac': 'http://server9.streamserver24.com:18920/motherearth.instrumental.aac', - 'url_flac': 'http://server9.streamserver24.com:18920/motherearth.instrumental', + 'url_aac': 'https://motherearth.streamserver24.com/listen/motherearth_instrumental/motherearth.instrumental.aac', + 'url_flac': 'https://motherearth.streamserver24.com/listen/motherearth_instrumental/motherearth.instrumental', + }, + { + 'channel': 3, + 'title': 'Mother Earth Jazz', + 'url_aac': 'https://motherearth.streamserver24.com/listen/motherearth_jazz/motherearth.jazz.mp4', + 'url_flac': 'https://motherearth.streamserver24.com/listen/motherearth_jazz/motherearth.jazz', }, ] STREAM_INFO = {s['url_aac']: s for s in STREAMS} @@ -74,12 +80,12 @@ def update(self): now = time.time() if now < self.next_update: return - res = requests.get(self.url, timeout=2) - res.raise_for_status() - data = res.json() + response = requests.get(self.url, timeout=2) + response.raise_for_status() + data = response.json() current = None songs = {} - key = build_key((data['now_playing']['song']['artist'], data['now_playing']['song']['title'])) + key = build_key((data['now_playing']['song']['artist'], data['now_playing']['song']['title'])) songs[key] = { 'artist': data['now_playing']['song']['artist'], 'cover': data['now_playing']['song']['art'], @@ -88,6 +94,7 @@ def update(self): 'album': data['now_playing']['song']['album'], } current = songs[key] + self._current = current self.songs = songs self.next_update = now + data['now_playing']['remaining'] diff --git a/script.audio.motherearth/resources/icon.png b/script.audio.motherearth/resources/icon.png index c770da139..81b4f4093 100644 Binary files a/script.audio.motherearth/resources/icon.png and b/script.audio.motherearth/resources/icon.png differ diff --git a/script.audio.motherearth/resources/language/resource.language.en_gb/strings.po b/script.audio.motherearth/resources/language/resource.language.en_gb/strings.po index ae10f71d2..4877c774d 100644 --- a/script.audio.motherearth/resources/language/resource.language.en_gb/strings.po +++ b/script.audio.motherearth/resources/language/resource.language.en_gb/strings.po @@ -28,3 +28,7 @@ msgstr "" msgctxt "#30205" msgid "Mother Earth Instrumental" msgstr "" + +msgctxt "#30206" +msgid "Mother Earth Jazz" +msgstr "" \ No newline at end of file diff --git a/script.audio.motherearth/resources/settings.xml b/script.audio.motherearth/resources/settings.xml index c5de23604..e7472a691 100644 --- a/script.audio.motherearth/resources/settings.xml +++ b/script.audio.motherearth/resources/settings.xml @@ -5,7 +5,7 @@ 0 - aac + flac @@ -23,6 +23,7 @@ + diff --git a/script.audio.motherearth/resources/skins/Default/media/logo.png b/script.audio.motherearth/resources/skins/Default/media/logo.png index 954d7f03a..c64ab5da2 100644 Binary files a/script.audio.motherearth/resources/skins/Default/media/logo.png and b/script.audio.motherearth/resources/skins/Default/media/logo.png differ diff --git a/script.audio.motherearth/resources/skins/Default/media/menu-focus.png b/script.audio.motherearth/resources/skins/Default/media/menu-focus.png index 3837a2b50..db92fb786 100644 Binary files a/script.audio.motherearth/resources/skins/Default/media/menu-focus.png and b/script.audio.motherearth/resources/skins/Default/media/menu-focus.png differ diff --git a/script.audio.motherearth/service.py b/script.audio.motherearth/service.py index 4a90c71e1..797d4b322 100644 --- a/script.audio.motherearth/service.py +++ b/script.audio.motherearth/service.py @@ -3,17 +3,17 @@ import xbmc import xbmcaddon import xbmcgui +import urllib from collections import namedtuple from motherearth import STREAM_INFO, NowPlaying - RESTART_INTERVAL = 1.0 RESTART_TIMEOUT = 1.0 +FANART_URL = "https://motherearthradio.de/artist/{}/fanart.jpg" Song = namedtuple('Song', 'data cover') - class Player(xbmc.Player): """Adds xbmc.Player callbacks and integrates with the API.""" @@ -84,12 +84,12 @@ def update_player(self): rating = float(song.data['rating']) info['rating'] = rating info['userrating'] = int(round(rating)) - if 'year' in song.data: - info['year'] = int(song.data['year']) item = xbmcgui.ListItem() item.setPath(self.getPlayingFile()) item.setArt({'thumb': song.cover}) - item.setArt({'fanart': song.cover}) + fanart = urllib.parse.quote(song.data['artist']) + fanart = FANART_URL.format(fanart) + item.setArt({'fanart' : fanart}) item.setInfo('music', info) self.updateInfoTag(item) diff --git a/script.cu.lrclyrics/addon.xml b/script.cu.lrclyrics/addon.xml index 206e22d01..e2d6dd86a 100644 --- a/script.cu.lrclyrics/addon.xml +++ b/script.cu.lrclyrics/addon.xml @@ -1,5 +1,5 @@ - + @@ -80,6 +80,6 @@ resources/icon.png - - remove invalid characters from filenames + - support for internet radio stations must now be enabled through settings diff --git a/script.cu.lrclyrics/changelog.txt b/script.cu.lrclyrics/changelog.txt index 78340c2d8..b6bd5fe91 100644 --- a/script.cu.lrclyrics/changelog.txt +++ b/script.cu.lrclyrics/changelog.txt @@ -1,3 +1,33 @@ +v6.6.6 +- fixed several scrapers + +v6.6.5 +- added "%A - %B - %N - %T" file format + +v6.6.4 +- added "%N. %A - %T" file format + +v6.6.3 +- fix issue with empty ListItem.Property(part1) +- fix for spotify (and others) not in sync +- don't search online sources for saved lyrics files + +v6.6.2 +- improve megalobiz scraper + +v6.6.1 +- add supermusic scraper + +v6.6.0 +- removed minilyrics +- removed gomaudio +- fixed lyricsify +- fixed lyricscom +- fixed azlyrics +- added lrclib +- added megalobiz +- added musixmatch lrc + v6.5.2 - remove invalid characters from filenames diff --git a/script.cu.lrclyrics/lib/culrcscrapers/gomaudio/__init__.py b/script.cu.lrclyrics/lib/broken-scrapers/gomaudio/__init__.py similarity index 100% rename from script.cu.lrclyrics/lib/culrcscrapers/gomaudio/__init__.py rename to script.cu.lrclyrics/lib/broken-scrapers/gomaudio/__init__.py diff --git a/script.cu.lrclyrics/lib/culrcscrapers/gomaudio/lyricsScraper.py b/script.cu.lrclyrics/lib/broken-scrapers/gomaudio/lyricsScraper.py similarity index 100% rename from script.cu.lrclyrics/lib/culrcscrapers/gomaudio/lyricsScraper.py rename to script.cu.lrclyrics/lib/broken-scrapers/gomaudio/lyricsScraper.py diff --git a/script.cu.lrclyrics/lib/culrcscrapers/minilyrics/__init__.py b/script.cu.lrclyrics/lib/broken-scrapers/minilyrics/__init__.py similarity index 100% rename from script.cu.lrclyrics/lib/culrcscrapers/minilyrics/__init__.py rename to script.cu.lrclyrics/lib/broken-scrapers/minilyrics/__init__.py diff --git a/script.cu.lrclyrics/lib/culrcscrapers/minilyrics/lyricsScraper.py b/script.cu.lrclyrics/lib/broken-scrapers/minilyrics/lyricsScraper.py similarity index 100% rename from script.cu.lrclyrics/lib/culrcscrapers/minilyrics/lyricsScraper.py rename to script.cu.lrclyrics/lib/broken-scrapers/minilyrics/lyricsScraper.py diff --git a/script.cu.lrclyrics/lib/culrcscrapers/azlyrics/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/azlyrics/lyricsScraper.py index b9d005f09..7fcf294f0 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/azlyrics/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/azlyrics/lyricsScraper.py @@ -33,7 +33,7 @@ def get_lyrics(self, song): return None req.close() try: - lyricscode = response.split('. -->')[1].split('')[1].split('', '\n') lyr = re.sub('<[^<]+?>', '', lyricstext) lyrics.lyrics = lyr diff --git a/script.cu.lrclyrics/lib/culrcscrapers/darklyrics/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/darklyrics/lyricsScraper.py index 206117752..d24a32bd6 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/darklyrics/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/darklyrics/lyricsScraper.py @@ -18,7 +18,7 @@ pass __title__ = 'darklyrics' -__priority__ = '250' +__priority__ = '260' __lrc__ = False diff --git a/script.cu.lrclyrics/lib/culrcscrapers/lrclib/__init__.py b/script.cu.lrclyrics/lib/culrcscrapers/lrclib/__init__.py new file mode 100644 index 000000000..b93054b3e --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/lrclib/__init__.py @@ -0,0 +1 @@ +# Dummy file to make this directory a package. diff --git a/script.cu.lrclyrics/lib/culrcscrapers/lrclib/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/lrclib/lyricsScraper.py new file mode 100644 index 000000000..5f4583447 --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/lrclib/lyricsScraper.py @@ -0,0 +1,66 @@ +#-*- coding: UTF-8 -*- +''' +Scraper for https://lrclib.net/ + +lrclib + +https://github.com/rtcq/syncedlyrics +''' + +import requests +import difflib +from lib.utils import * + +__title__ = "lrclib" +__priority__ = '110' +__lrc__ = True + + +class LyricsFetcher: + def __init__(self, *args, **kwargs): + self.DEBUG = kwargs['debug'] + self.settings = kwargs['settings'] + self.SEARCH_URL = 'https://lrclib.net/api/search?q=%s-%s' + self.LYRIC_URL = 'https://lrclib.net/api/get/%i' + + def get_lyrics(self, song): + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + try: + url = self.SEARCH_URL % (song.artist, song.title) + response = requests.get(url, timeout=10) + result = response.json() + except: + return None + links = [] + for item in result: + artistname = item['artistName'] + songtitle = item['name'] + songid = item['id'] + if (difflib.SequenceMatcher(None, song.artist.lower(), artistname.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, song.title.lower(), songtitle.lower()).ratio() > 0.8): + links.append((artistname + ' - ' + songtitle, self.LYRIC_URL % songid, artistname, songtitle)) + if len(links) == 0: + return None + elif len(links) > 1: + lyrics.list = links + for link in links: + lyr = self.get_lyrics_from_list(link) + if lyr: + lyrics.lyrics = lyr + return lyrics + return None + + def get_lyrics_from_list(self, link): + title,url,artist,song = link + try: + log('%s: search url: %s' % (__title__, url), debug=self.DEBUG) + response = requests.get(url, timeout=10) + result = response.json() + except: + return None + if 'syncedLyrics' in result: + lyrics = result['syncedLyrics'] + return lyrics diff --git a/script.cu.lrclyrics/lib/culrcscrapers/lyricscom/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/lyricscom/lyricsScraper.py index 2e04e6537..6b2dacab9 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/lyricscom/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/lyricscom/lyricsScraper.py @@ -10,21 +10,23 @@ __priority__ = '240' __lrc__ = False +UserAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"} class LyricsFetcher: def __init__(self, *args, **kwargs): self.DEBUG = kwargs['debug'] self.settings = kwargs['settings'] - self.url = 'http://www.lyrics.com/serp.php?st=%s&qtype=2' + self.url = 'https://www.lyrics.com/serp.php?st=%s&qtype=2' def get_lyrics(self, song): + sess = requests.Session() log('%s: searching lyrics for %s - %s' % (__title__, song.artist, song.title), debug=self.DEBUG) lyrics = Lyrics(settings=self.settings) lyrics.song = song lyrics.source = __title__ lyrics.lrc = __lrc__ try: - request = requests.get(self.url % urllib.parse.quote_plus(song.artist), timeout=10) + request = sess.get(self.url % urllib.parse.quote_plus(song.artist), headers=UserAgent, timeout=10) response = request.text except: return @@ -32,11 +34,11 @@ def get_lyrics(self, song): url = '' for link in soup.find_all('a'): if link.string and link.get('href').startswith('artist/'): - url = 'http://www.lyrics.com/' + link.get('href') + url = 'https://www.lyrics.com/' + link.get('href') break if url: try: - req = requests.get(url, timeout=10) + req = sess.get(url, headers=UserAgent, timeout=10) resp = req.text except: return @@ -44,11 +46,11 @@ def get_lyrics(self, song): url = '' for link in soup.find_all('a'): if link.string and (difflib.SequenceMatcher(None, link.string.lower(), song.title.lower()).ratio() > 0.8): - url = 'http://www.lyrics.com' + link.get('href') + url = 'https://www.lyrics.com' + link.get('href') break if url: try: - req2 = requests.get(url, timeout=10) + req2 = sess.get(url, headers=UserAgent, timeout=10) resp2 = req2.text except: return diff --git a/script.cu.lrclyrics/lib/culrcscrapers/lyricsify/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/lyricsify/lyricsScraper.py index 23d063942..686709db9 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/lyricsify/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/lyricsify/lyricsScraper.py @@ -13,14 +13,15 @@ __priority__ = '130' __lrc__ = True -UserAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"} +UserAgent = {"Host": "www.lyricsify.com", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br, zstd", "DNT": "1", "Alt-Used": "www.lyricsify.com", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1", "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Sec-Fetch-User": "?1", "Priority": "u=1"} +# lyricsify uses captcha's & cloudflare protection for the search option, only direct lyrics access works class LyricsFetcher: def __init__(self, *args, **kwargs): self.DEBUG = kwargs['debug'] self.settings = kwargs['settings'] - self.SEARCH_URL = 'https://www.lyricsify.com/search?q=%s' + self.SEARCH_URL = 'https://www.lyricsify.com/lyrics/%s/%s' self.LYRIC_URL = 'https://www.lyricsify.com%s' def get_lyrics(self, song): @@ -29,11 +30,34 @@ def get_lyrics(self, song): lyrics.song = song lyrics.source = __title__ lyrics.lrc = __lrc__ - artist = song.artist.replace(' ', '+') - title = song.title.replace(' ', '+') - search = '%s+%s' % (artist, title) + artist = song.artist.replace("'", '').replace('!', '').replace('?', '').replace('"', '').replace('/', '').replace('.', '').replace('&', '').replace(',', '').replace('(', '').replace(')', '').replace(' ', '-') + title = song.title.replace("'", '').replace('!', '').replace('?', '').replace('"', '').replace('/', '').replace('.', '').replace('&', '').replace(',', '').replace('(', '').replace(')', '').replace(' ', '-') + url = self.SEARCH_URL % (artist.lower(), title.lower()) try: - url = self.SEARCH_URL % search + log('%s: search url: %s' % (__title__, url), debug=self.DEBUG) + search = requests.get(url, headers=UserAgent, timeout=10) + response = search.text + except: + return None + matchcode = re.search('details">(.*?)', '', lyricscode) + lyrics.lyrics = lyr + return lyrics + return None + +''' + def get_lyrics(self, song): + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + artist = song.artist.replace(' ', '-') + title = song.title.replace(' ', '-') + try: + url = self.SEARCH_URL % (artist, title) search = requests.get(url, headers=UserAgent, timeout=10) response = search.text except: @@ -41,7 +65,7 @@ def get_lyrics(self, song): links = [] soup = BeautifulSoup(response, 'html.parser') for link in soup.find_all('a'): - if link.string and link.get('href').startswith('/lyrics/'): + if link.string and link.get('href').startswith('/lrc/'): foundartist = link.string.split(' - ', 1)[0] # some links don't have a proper 'artist - title' format try: @@ -69,8 +93,9 @@ def get_lyrics_from_list(self, link): response = search.text except: return None - matchcode = re.search('div id="entry">(.*?)(.*?)', '', lyricscode) return cleanlyrics +''' diff --git a/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/__init__.py b/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/__init__.py new file mode 100644 index 000000000..b93054b3e --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/__init__.py @@ -0,0 +1 @@ +# Dummy file to make this directory a package. diff --git a/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/lyricsScraper.py new file mode 100644 index 000000000..43c8b21ff --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/megalobiz/lyricsScraper.py @@ -0,0 +1,69 @@ +#-*- coding: UTF-8 -*- +''' +Scraper for https://www.megalobiz.com/ + +megalobiz + +https://github.com/rtcq/syncedlyrics +''' + +import requests +import re +from bs4 import BeautifulSoup +from lib.utils import * + +__title__ = "Megalobiz" +__priority__ = '150' +__lrc__ = True + + +class LyricsFetcher: + def __init__(self, *args, **kwargs): + self.DEBUG = kwargs['debug'] + self.settings = kwargs['settings'] + self.SEARCH_URL = 'https://www.megalobiz.com/search/all?qry=%s-%s&searchButton.x=0&searchButton.y=0' + self.LYRIC_URL = 'https://www.megalobiz.com/%s' + + def get_lyrics(self, song): + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + try: + url = self.SEARCH_URL % (song.artist, song.title) + response = requests.get(url, timeout=10) + result = response.text + except: + return None + links = [] + soup = BeautifulSoup(result, 'html.parser') + for link in soup.find_all('a'): + if link.get('href') and link.get('href').startswith('/lrc/maker/'): + linktext = link.text.replace('_', ' ').strip() + if song.artist.lower() in linktext.lower() and song.title.lower() in linktext.lower(): + links.append((linktext, self.LYRIC_URL % link.get('href'), song.artist, song.title)) + if len(links) == 0: + return None + elif len(links) > 1: + lyrics.list = links + for link in links: + lyr = self.get_lyrics_from_list(link) + if lyr: + lyrics.lyrics = lyr + return lyrics + return None + + def get_lyrics_from_list(self, link): + title,url,artist,song = link + try: + log('%s: search url: %s' % (__title__, url), debug=self.DEBUG) + response = requests.get(url, timeout=10) + result = response.text + except: + return None + matchcode = re.search('span id="lrc_[0-9]+_lyrics">(.*?)', '', lyricscode) + return cleanlyrics diff --git a/script.cu.lrclyrics/lib/culrcscrapers/music163/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/music163/lyricsScraper.py index 8ef1deb23..3f546399f 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/music163/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/music163/lyricsScraper.py @@ -45,8 +45,9 @@ def get_lyrics(self, song): links = [] if 'result' in result and 'songs' in result['result']: for item in result['result']['songs']: - if (difflib.SequenceMatcher(None, artist.lower(), item['artists'][0]['name'].lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), item['name'].lower()).ratio() > 0.8): - links.append((item['artists'][0]['name'] + ' - ' + item['name'], self.LYRIC_URL + '?id=' + str(item['id']) + '&lv=-1&kv=-1&tv=-1', item['artists'][0]['name'], item['name'])) + artists = "+&+".join([a["name"] for a in item["artists"]]) + if (difflib.SequenceMatcher(None, artist.lower(), artists.lower()).ratio() > 0.6) and (difflib.SequenceMatcher(None, title.lower(), item['name'].lower()).ratio() > 0.8): + links.append((artists + ' - ' + item['name'], self.LYRIC_URL + '?id=' + str(item['id']) + '&lv=-1&kv=-1&tv=-1', artists, item['name'])) if len(links) == 0: return None elif len(links) > 1: diff --git a/script.cu.lrclyrics/lib/culrcscrapers/musixmatch/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/musixmatch/lyricsScraper.py index 60e6e36b8..1b1aa1f3c 100644 --- a/script.cu.lrclyrics/lib/culrcscrapers/musixmatch/lyricsScraper.py +++ b/script.cu.lrclyrics/lib/culrcscrapers/musixmatch/lyricsScraper.py @@ -10,6 +10,7 @@ import re import random import difflib +import html from bs4 import BeautifulSoup from lib.utils import * @@ -20,14 +21,40 @@ headers = {} headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0' +# search is not possible as it requires javascript, only direct access to the lyrics work. class LyricsFetcher: def __init__(self, *args, **kwargs): self.DEBUG = kwargs['debug'] self.settings = kwargs['settings'] - self.SEARCH_URL = 'https://www.musixmatch.com/search/' - self.LYRIC_URL = 'https://www.musixmatch.com' + self.SEARCH_URL = 'https://www.musixmatch.com/search?query=' + self.LYRIC_URL = 'https://www.musixmatch.com/lyrics/%s/%s' + def get_lyrics(self, song): + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + artist = song.artist.replace("'", '').replace('!', '').replace('?', '').replace('"', '').replace('/', '').replace('.', '').replace('&', '').replace(',', '').replace('(', '').replace(')', '').replace(' ', '-') + title = song.title.replace("'", '').replace('!', '').replace('?', '').replace('"', '').replace('/', '').replace('.', '').replace('&', '').replace(',', '').replace('(', '').replace(')', '').replace(' ', '-') + url = self.LYRIC_URL % (artist, title) + try: + log('%s: search url: %s' % (__title__, url), debug=self.DEBUG) + search = requests.get(url, headers=headers, timeout=10) + response = search.text + except: + return None + matchcode = re.search('Lyrics of (.*?)Writer\(s\): ', response, flags=re.DOTALL) + if matchcode: + lyricscode = (matchcode.group(1)) + lyr = re.sub('<[^<]+?>', '\n', lyricscode) + lyr = html.unescape(lyr) + lyrics.lyrics = lyr.replace('\n\n\n\n', '\n') + return lyrics + return None + +''' def get_lyrics(self, song): log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) lyrics = Lyrics(settings=self.settings) @@ -84,3 +111,4 @@ def get_lyrics_from_list(self, link): for part in lyr: lyrics = lyrics + part.get_text() + '\n' return lyrics +''' diff --git a/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/__init__.py b/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/__init__.py new file mode 100644 index 000000000..b93054b3e --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/__init__.py @@ -0,0 +1 @@ +# Dummy file to make this directory a package. diff --git a/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/lyricsScraper.py new file mode 100644 index 000000000..74a7598ad --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/musixmatchlrc/lyricsScraper.py @@ -0,0 +1,117 @@ +#-*- coding: UTF-8 -*- +''' +Scraper for https://www.musixmatch.com/ + +musixmatchlrc + +https://github.com/rtcq/syncedlyrics +''' + +import requests +import json +import time +import difflib +import xbmcvfs +from lib.utils import * + +__title__ = "musixmatchlrc" +__priority__ = '100' +__lrc__ = True + + +class LyricsFetcher: + def __init__(self, *args, **kwargs): + self.DEBUG = kwargs['debug'] + self.settings = kwargs['settings'] + self.SEARCH_URL = 'https://apic-desktop.musixmatch.com/ws/1.1/%s' + self.session = requests.Session() + self.session.headers.update( + { + "authority": "apic-desktop.musixmatch.com", + "cookie": "AWSELBCORS=0; AWSELB=0", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0", + } + ) + self.current_time = int(time.time()) + + def get_token(self): + self.token = '' + tokenpath = os.path.join(PROFILE, 'musixmatch_token') + if xbmcvfs.exists(tokenpath): + tokenfile = xbmcvfs.File(tokenpath) + tokendata = json.load(tokenfile) + tokenfile.close() + cached_token = tokendata.get("token") + expiration_time = tokendata.get("expiration_time") + if cached_token and expiration_time and self.current_time < expiration_time: + self.token = cached_token + if not self.token: + try: + url = self.SEARCH_URL % 'token.get' + query = [('user_language', 'en'), ('app_id', 'web-desktop-app-v1.0'), ('t', self.current_time)] + response = self.session.get(url, params=query, timeout=10) + result = response.json() + except: + return None + if 'message' in result and 'body' in result["message"] and 'user_token' in result["message"]["body"]: + self.token = result["message"]["body"]["user_token"] + expiration_time = self.current_time + 600 + tokendata = {} + tokendata['token'] = self.token + tokendata['expiration_time'] = expiration_time + tokenfile = xbmcvfs.File(tokenpath, 'w') + json.dump(tokendata, tokenfile) + tokenfile.close() + return self.token + + def get_lyrics(self, song): + self.token = self.get_token() + if not self.token: + return + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + artist = song.artist.replace(' ', '+') + title = song.title.replace(' ', '+') + search = '%s - %s' % (artist, title) + try: + url = self.SEARCH_URL % 'track.search' + query = [('q', search), ('page_size', '5'), ('page', '1'), ('app_id', 'web-desktop-app-v1.0'), ('usertoken', self.token), ('t', self.current_time)] + response = requests.get(url, params=query, timeout=10) + result = response.json() + except: + return None + links = [] + if 'message' in result and 'body' in result["message"] and 'track_list' in result["message"]["body"] and result["message"]["body"]["track_list"]: + for item in result["message"]["body"]["track_list"]: + artistname = item['track']['artist_name'] + songtitle = item['track']['track_name'] + trackid = item['track']['track_id'] + if (difflib.SequenceMatcher(None, artist.lower(), artistname.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), songtitle.lower()).ratio() > 0.8): + links.append((artistname + ' - ' + songtitle, trackid, artistname, songtitle)) + if len(links) == 0: + return None + elif len(links) > 1: + lyrics.list = links + for link in links: + lyr = self.get_lyrics_from_list(link) + if lyr: + lyrics.lyrics = lyr + return lyrics + return None + + def get_lyrics_from_list(self, link): + title,trackid,artist,song = link + try: + log('%s: search track id: %s' % (__title__, trackid), debug=self.DEBUG) + url = self.SEARCH_URL % 'track.subtitle.get' + query = [('track_id', trackid), ('subtitle_format', 'lrc'), ('app_id', 'web-desktop-app-v1.0'), ('usertoken', self.token), ('t', self.current_time)] + response = requests.get(url, params=query, timeout=10) + result = response.json() + except: + return None + if 'message' in result and 'body' in result["message"] and 'subtitle' in result["message"]["body"] and 'subtitle_body' in result["message"]["body"]["subtitle"]: + lyrics = result["message"]["body"]["subtitle"]["subtitle_body"] + return lyrics diff --git a/script.cu.lrclyrics/lib/culrcscrapers/rclyricsband/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/rclyricsband/lyricsScraper.py new file mode 100644 index 000000000..851986508 --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/rclyricsband/lyricsScraper.py @@ -0,0 +1,71 @@ +#-*- coding: UTF-8 -*- +''' +Scraper for https://www.rclyricsband.com/ +''' + +import requests +import re +import difflib +from bs4 import BeautifulSoup +from lib.utils import * + +__title__ = "RCLyricsBand" +__priority__ = '140' +__lrc__ = True + +UserAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"} + +class LyricsFetcher: + def __init__(self, *args, **kwargs): + self.DEBUG = kwargs['debug'] + self.settings = kwargs['settings'] + self.SEARCH_URL = 'https://rclyricsband.com/?search=%s %s' + self.LYRIC_URL = 'https://rclyricsband.com/%s' + + + def get_lyrics(self, song): + log("%s: searching lyrics for %s - %s" % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + artist = song.artist + title = song.title + try: + url = self.SEARCH_URL % (artist, title) + search = requests.get(url, headers=UserAgent, timeout=10) + response = search.text + except: + return None + links = [] + soup = BeautifulSoup(response, 'html.parser') + for link in soup.find_all('a', {'class': 'song_search'}): + if link.string: + foundsong = link.string.split(' - ')[0] + foundartist = link.string.split(' - ')[-1] + if (difflib.SequenceMatcher(None, artist.lower(), foundartist.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), foundsong.lower()).ratio() > 0.8): + links.append((foundartist + ' - ' + foundsong, self.LYRIC_URL % link.get('href'), foundartist, foundsong)) + if len(links) == 0: + return None + elif len(links) > 1: + lyrics.list = links + for link in links: + lyr = self.get_lyrics_from_list(link) + if lyr: + lyrics.lyrics = lyr + return lyrics + return None + + def get_lyrics_from_list(self, link): + title,url,artist,song = link + try: + log('%s: search url: %s' % (__title__, url), debug=self.DEBUG) + search = requests.get(url, headers=UserAgent, timeout=10) + response = search.text + except: + return None + matchcode = re.search("lrc_text_format'>(.*?)', '', lyricscode) + return cleanlyrics diff --git a/script.cu.lrclyrics/lib/culrcscrapers/supermusic/__init__.py b/script.cu.lrclyrics/lib/culrcscrapers/supermusic/__init__.py new file mode 100644 index 000000000..b93054b3e --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/supermusic/__init__.py @@ -0,0 +1 @@ +# Dummy file to make this directory a package. diff --git a/script.cu.lrclyrics/lib/culrcscrapers/supermusic/lyricsScraper.py b/script.cu.lrclyrics/lib/culrcscrapers/supermusic/lyricsScraper.py new file mode 100644 index 000000000..e6f556d63 --- /dev/null +++ b/script.cu.lrclyrics/lib/culrcscrapers/supermusic/lyricsScraper.py @@ -0,0 +1,64 @@ +#-*- coding: UTF-8 -*- +import sys +import re +import requests +import html +import xbmc +import xbmcaddon +from lib.utils import * + +__title__ = 'supermusic' +__priority__ = '250' +__lrc__ = False + + +class LyricsFetcher: + def __init__(self, *args, **kwargs): + self.DEBUG = kwargs['debug'] + self.settings = kwargs['settings'] + + def get_lyrics(self, song): + log('%s: searching lyrics for %s - %s' % (__title__, song.artist, song.title), debug=self.DEBUG) + lyrics = Lyrics(settings=self.settings) + lyrics.song = song + lyrics.source = __title__ + lyrics.lrc = __lrc__ + artist = song.artist.lower() + title = song.title.lower() + + try: + req = requests.post('https://supermusic.cz/najdi.php', data={'hladane': title, 'typhladania': 'piesen', 'fraza': 'off'}) + response = req.text + except: + return None + req.close() + url = None + try: + items = re.search(r'Počet nájdených piesní.+

(.*)
', response, re.S).group(1) + for match in re.finditer(r'"[^"]+?") target="_parent">(?P.*?) - (?P.+?) \((.*?)', response, re.S).group(1) + lyr = re.sub(r'.*?', '', lyr) + lyr = re.sub(r'\s*', '\n', lyr) + lyr = re.sub(r'', '', lyr, flags=re.DOTALL) + lyr = re.sub(r'<[^>]*?>', '', lyr, flags=re.DOTALL) + lyr = lyr.strip('\r\n') + lyr = html.unescape(lyr) + lyrics.lyrics = lyr + return lyrics + except: + return None diff --git a/script.cu.lrclyrics/lib/gui.py b/script.cu.lrclyrics/lib/gui.py index f18036cb2..c9178eee0 100644 --- a/script.cu.lrclyrics/lib/gui.py +++ b/script.cu.lrclyrics/lib/gui.py @@ -53,6 +53,7 @@ def get_settings(self): self.SETTING_SAVE_SUBFOLDER = ADDON.getSettingBool('save_subfolder') self.SETTING_SAVE_SUBFOLDER_PATH = ADDON.getSettingString('save_subfolder_path') self.SETTING_CLEAN_TITLE = ADDON.getSettingBool('clean_title') + self.SETTING_INTERNETRADIO = ADDON.getSettingBool('internetradio') self.lyricssettings = {} self.lyricssettings['debug'] = self.DEBUG self.lyricssettings['read_filename'] = self.SETTING_READ_FILENAME @@ -84,7 +85,7 @@ def main_loop(self): # do not try and get lyrics for any background media if self.proceed(): if not self.CULRC_FIRSTRUN: - # only the first lyrics are fetched by main_loop, the rest is done through onAVChanged. this makes sure both don't run simultaniously + # only the first lyrics are fetched by main_loop, the rest is done through onAVStarted. this makes sure both don't run simultaniously self.CULRC_FIRSTRUN = True # notify user the script is searching for lyrics if not self.SETTING_SILENT: @@ -96,7 +97,10 @@ def main_loop(self): WIN.setProperty('culrc.force','FALSE') self.current_lyrics = Lyrics(settings=self.lyricssettings) self.myPlayerChanged() - elif xbmc.getCondVisibility('Player.IsInternetStream'): + # internetstreams may (like spotify) or may not (like many internet radio stations) generate onAVStarted callbacks to indicate a new song has started. + # TODO: no idea how to differentiate between those automatically. + # for now, add a setting for internet radio, which will cause issues with spotify and the likes. + elif xbmc.getCondVisibility('Player.IsInternetStream') and self.SETTING_INTERNETRADIO: self.myPlayerChanged() else: # we may have exited the music visualization screen, reset current lyrics so we show them again when re-entering the visualization screen @@ -224,6 +228,9 @@ def get_lyrics_from_file(self, song, getlrc): # Search same path with song file lyricsfile = song.path2(getlrc) log('path2: %s' % lyricsfile, debug=self.DEBUG) + # don't search online sources for saved lyrics files + if xbmc.getCondVisibility('Player.IsInternetStream') or xbmc.getCondVisibility('Pvr.IsPlayingRadio'): + return None if xbmcvfs.exists(lyricsfile): lyr = get_textfile(lyricsfile) if lyr != None: @@ -568,8 +575,9 @@ def show_lyrics(self, lyrics): WIN.setProperty('culrc.islrc', 'true') self.parser_lyrics(lyrics.lyrics) for num, (time, line) in enumerate(self.pOverlay): - parts = self.get_parts(line) - listitem = xbmcgui.ListItem(line, offscreen=True) + cleanline = line.strip() + parts = self.get_parts(cleanline) + listitem = xbmcgui.ListItem(cleanline, offscreen=True) for count, item in enumerate(parts): listitem.setProperty('part%i' % (count + 1), item) delta = 100000 # in case duration of the last line is undefined @@ -582,8 +590,9 @@ def show_lyrics(self, lyrics): WIN.clearProperty('culrc.islrc') splitLyrics = lyrics.lyrics.splitlines() for line in splitLyrics: - parts = self.get_parts(line) - listitem = xbmcgui.ListItem(line, offscreen=True) + cleanline = line.strip() + parts = self.get_parts(cleanline) + listitem = xbmcgui.ListItem(cleanline, offscreen=True) for count, item in enumerate(parts): listitem.setProperty('part%i' % (count + 1), item) self.text.addItem(listitem) diff --git a/script.cu.lrclyrics/lib/scrapertest.py b/script.cu.lrclyrics/lib/scrapertest.py index d803664ac..2ced98b97 100644 --- a/script.cu.lrclyrics/lib/scrapertest.py +++ b/script.cu.lrclyrics/lib/scrapertest.py @@ -4,13 +4,16 @@ from lib.culrcscrapers.azlyrics import lyricsScraper as lyricsScraper_azlyrics from lib.culrcscrapers.darklyrics import lyricsScraper as lyricsScraper_darklyrics from lib.culrcscrapers.genius import lyricsScraper as lyricsScraper_genius -from lib.culrcscrapers.gomaudio import lyricsScraper as lyricsScraper_gomaudio +from lib.culrcscrapers.lrclib import lyricsScraper as lyricsScraper_lrclib from lib.culrcscrapers.lyricscom import lyricsScraper as lyricsScraper_lyricscom from lib.culrcscrapers.lyricsify import lyricsScraper as lyricsScraper_lyricsify from lib.culrcscrapers.lyricsmode import lyricsScraper as lyricsScraper_lyricsmode -from lib.culrcscrapers.minilyrics import lyricsScraper as lyricsScraper_minilyrics +from lib.culrcscrapers.megalobiz import lyricsScraper as lyricsScraper_megalobiz from lib.culrcscrapers.music163 import lyricsScraper as lyricsScraper_music163 from lib.culrcscrapers.musixmatch import lyricsScraper as lyricsScraper_musixmatch +from lib.culrcscrapers.musixmatchlrc import lyricsScraper as lyricsScraper_musixmatchlrc +from lib.culrcscrapers.rclyricsband import lyricsScraper as lyricsScraper_rclyricsband +from lib.culrcscrapers.supermusic import lyricsScraper as lyricsScraper_supermusic FAILED = [] @@ -45,7 +48,7 @@ def test_scrapers(): return # test darklyrics - dialog.update(11, LANGUAGE(32163) % 'darklyrics') + dialog.update(7, LANGUAGE(32163) % 'darklyrics') log('==================== darklyrics ====================', debug=True) song = Song(opt=lyricssettings) song.artist = 'Neurosis' @@ -64,7 +67,7 @@ def test_scrapers(): return # test genius - dialog.update(22, LANGUAGE(32163) % 'genius') + dialog.update(14, LANGUAGE(32163) % 'genius') log('==================== genius ====================', debug=True) song = Song(opt=lyricssettings) song.artist = 'Maren Morris' @@ -82,27 +85,27 @@ def test_scrapers(): if dialog.iscanceled(): return - # test gomaudio - dialog.update(33, LANGUAGE(32163) % 'gomaudio') - log('==================== gomaudio ====================', debug=True) + # test lrclib + dialog.update(21, LANGUAGE(32163) % 'lrclib') + log('==================== lrclib ====================', debug=True) song = Song(opt=lyricssettings) - song.artist = 'Lady Gaga' - song.title = 'Just Dance' + song.artist = 'CHVRCHES' + song.title = 'Clearest Blue' st = time.time() - lyrics = lyricsScraper_gomaudio.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song, 'd106534632cb43306423acb351f8e6e9', '.mp3') + lyrics = lyricsScraper_lrclib.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) ft = time.time() tt = ft - st - TIMINGS.append(['gomaudio',tt]) + TIMINGS.append(['lrclib',tt]) if lyrics: log(lyrics.lyrics, debug=True) else: - FAILED.append('gomaudio') - log('FAILED: gomaudio', debug=True) + FAILED.append('lrclib') + log('FAILED: lrclib', debug=True) if dialog.iscanceled(): return # test lyricscom - dialog.update(44, LANGUAGE(32163) % 'lyricscom') + dialog.update(28, LANGUAGE(32163) % 'lyricscom') log('==================== lyricscom ====================', debug=True) song = Song(opt=lyricssettings) song.artist = 'Blur' @@ -121,11 +124,11 @@ def test_scrapers(): return # test lyricsify - dialog.update(55, LANGUAGE(32163) % 'lyricsify') + dialog.update(35, LANGUAGE(32163) % 'lyricsify') log('==================== lyricsify ====================', debug=True) song = Song(opt=lyricssettings) - song.artist = 'Madonna' - song.title = 'Crazy For You' + song.artist = 'Tears For Fears' + song.title = 'Shout' st = time.time() lyrics = lyricsScraper_lyricsify.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) ft = time.time() @@ -140,7 +143,7 @@ def test_scrapers(): return # test lyricsmode - dialog.update(66, LANGUAGE(32163) % 'lyricsmode') + dialog.update(42, LANGUAGE(32163) % 'lyricsmode') log('==================== lyricsmode ====================', debug=True) song = Song(opt=lyricssettings) song.artist = 'Maren Morris' @@ -158,32 +161,31 @@ def test_scrapers(): if dialog.iscanceled(): return - - # test minilyrics - dialog.update(77, LANGUAGE(32163) % 'minilyrics') - log('==================== minilyrics ====================', debug=True) + # test megalobiz + dialog.update(50, LANGUAGE(32163) % 'megalobiz') + log('==================== megalobiz ====================', debug=True) song = Song(opt=lyricssettings) - song.artist = 'Chicago' - song.title = 'Stay The Night' + song.artist = 'Michael Jackson' + song.title = 'Beat It' st = time.time() - lyrics = lyricsScraper_minilyrics.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) + lyrics = lyricsScraper_megalobiz.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) ft = time.time() tt = ft - st - TIMINGS.append(['minilyrics',tt]) + TIMINGS.append(['megalobiz',tt]) if lyrics: log(lyrics.lyrics, debug=True) else: - FAILED.append('minilyrics') - log('FAILED: minilyrics', debug=True) + FAILED.append('megalobiz') + log('FAILED: megalobiz', debug=True) if dialog.iscanceled(): return # test music163 - dialog.update(88, LANGUAGE(32163) % 'music163') + dialog.update(58, LANGUAGE(32163) % 'music163') log('==================== music163 ====================', debug=True) song = Song(opt=lyricssettings) - song.artist = 'Chicago' - song.title = 'Stay The Night' + song.artist = 'Madonna' + song.title = 'Vogue' st = time.time() lyrics = lyricsScraper_music163.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) ft = time.time() @@ -198,7 +200,7 @@ def test_scrapers(): return # test musixmatch - dialog.update(88, LANGUAGE(32163) % 'musixmatch') + dialog.update(66, LANGUAGE(32163) % 'musixmatch') log('==================== musixmatch ====================', debug=True) song = Song(opt=lyricssettings) song.artist = 'Kate Bush' @@ -216,6 +218,63 @@ def test_scrapers(): if dialog.iscanceled(): return + # test musixmatchlrc + dialog.update(73, LANGUAGE(32163) % 'musixmatchlrc') + log('==================== musixmatchlrc ====================', debug=True) + song = Song(opt=lyricssettings) + song.artist = 'Kate Bush' + song.title = 'Wuthering Heights' + st = time.time() + lyrics = lyricsScraper_musixmatchlrc.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) + ft = time.time() + tt = ft - st + TIMINGS.append(['musixmatchlrc',tt]) + if lyrics: + log(lyrics.lyrics, debug=True) + else: + FAILED.append('musixmatchlrc') + log('FAILED: musixmatchlrc', debug=True) + if dialog.iscanceled(): + return + + # test rclyricsband + dialog.update(80, LANGUAGE(32163) % 'rclyricsband') + log('==================== rclyricsband ====================', debug=True) + song = Song(opt=lyricssettings) + song.artist = 'Taylor Swift' + song.title = 'The Archer' + st = time.time() + lyrics = lyricsScraper_rclyricsband.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) + ft = time.time() + tt = ft - st + TIMINGS.append(['rclyricsband',tt]) + if lyrics: + log(lyrics.lyrics, debug=True) + else: + FAILED.append('rclyricsband') + log('FAILED: rclyricsband', debug=True) + if dialog.iscanceled(): + return + + # test supermusic + dialog.update(88, LANGUAGE(32163) % 'supermusic') + log('==================== supermusic ====================', debug=True) + song = Song(opt=lyricssettings) + song.artist = 'Karel Gott' + song.title = 'Trezor' + st = time.time() + lyrics = lyricsScraper_supermusic.LyricsFetcher(settings=lyricssettings, debug=True).get_lyrics(song) + ft = time.time() + tt = ft - st + TIMINGS.append(['supermusic',tt]) + if lyrics: + log(lyrics.lyrics, debug=True) + else: + FAILED.append('supermusic') + log('FAILED: supermusic', debug=True) + if dialog.iscanceled(): + return + dialog.close() log('=======================================', debug=True) log('FAILED: %s' % str(FAILED), debug=True) diff --git a/script.cu.lrclyrics/lib/utils.py b/script.cu.lrclyrics/lib/utils.py index 03f30cc3d..582485f3b 100644 --- a/script.cu.lrclyrics/lib/utils.py +++ b/script.cu.lrclyrics/lib/utils.py @@ -68,7 +68,7 @@ def get_artist_from_filename(*args, **kwargs): elif SETTING_READ_FILENAME_FORMAT == 2: title = os.path.splitext(basename)[0].split(' ', 1)[1].lstrip('-').strip() # Track Artist - title.ext - elif SETTING_READ_FILENAME_FORMAT == 3: + elif SETTING_READ_FILENAME_FORMAT in (3,5): at = basename.split(' ', 1)[1].strip() artist = at.split('-', 1)[0].strip() title = os.path.splitext(at.split('-', 1)[1].strip())[0] @@ -76,6 +76,9 @@ def get_artist_from_filename(*args, **kwargs): elif SETTING_READ_FILENAME_FORMAT == 4: artist = basename.split('-', 2)[1].strip() title = os.path.splitext(basename.split('-', 2)[2].strip())[0] + elif SETTING_READ_FILENAME_FORMAT == 6: + artist = basename.split('-', 1)[0].strip() + title = os.path.splitext(basename.split('-', 3)[3].strip())[0] except: # invalid format selected log('failed to get artist and title from filename', debug=DEBUG) diff --git a/script.cu.lrclyrics/resources/language/resource.language.af_za/strings.po b/script.cu.lrclyrics/resources/language/resource.language.af_za/strings.po index 919e2c073..6d202df3a 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.af_za/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.af_za/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.am_et/strings.po b/script.cu.lrclyrics/resources/language/resource.language.am_et/strings.po index ad2a585e9..6ccd3bcca 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.am_et/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.am_et/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ar_SA/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ar_SA/strings.po new file mode 100644 index 000000000..3c41f53d3 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.ar_SA/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ar_SA\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ar_sa/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ar_sa/strings.po index 03ff50daa..fd8580940 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ar_sa/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ar_sa/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ast_es/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ast_es/strings.po index dc10cea85..861af9952 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ast_es/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ast_es/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.az_az/strings.po b/script.cu.lrclyrics/resources/language/resource.language.az_az/strings.po index 95fda82ca..846c73c79 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.az_az/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.az_az/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.be_by/strings.po b/script.cu.lrclyrics/resources/language/resource.language.be_by/strings.po index 61a8e45b3..50911e00d 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.be_by/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.be_by/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.bg_bg/strings.po b/script.cu.lrclyrics/resources/language/resource.language.bg_bg/strings.po index 102588823..e4a592a08 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.bg_bg/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.bg_bg/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.bs_ba/strings.po b/script.cu.lrclyrics/resources/language/resource.language.bs_ba/strings.po index 152b3b30f..8f4f0d924 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.bs_ba/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.bs_ba/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ca_es/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ca_es/strings.po index 99a243c0b..903d97ba0 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ca_es/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ca_es/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Catalan (http://www.transifex.com/projects/p/xbmc-addons/language/ca/)\n" -"Language: ca\n" +"PO-Revision-Date: 2024-01-30 23:13+0000\n" +"Last-Translator: \"Pere O.\" \n" +"Language-Team: Catalan (Spain) \n" +"Language: ca_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -22,7 +23,7 @@ msgstr "CU LRC Lyrics" msgctxt "Addon Description" msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." -msgstr "CU LRC Lyrics es un script de lletres de cançons per a Kodi. Suporta lletres normal tan com LRC Lyrics. El script busca lletres sincronitzades o no, incrustades al fitxer d'àudio, des de un altre fitxer o per scrapers. Pot llegir fitxers .lrc/.txt guardats al mateix directori i nom de fitxer amb .mp3(o un altre tipus de música)." +msgstr "CU LRC Lyrics és un script de lletres de cançons per al Kodi. Suporta lletres normals tant com LRC Lyrics. L'script busca lletres sincronitzades o no, incrustades al fitxer d'àudio, des d'un altre fitxer o per raspadors. Pot llegir fitxers .lrc/.txt guardats al mateix directori i nom de fitxer amb .mp3 (o un altre format de música)." # Script labels msgctxt "#32000" @@ -147,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -163,7 +172,7 @@ msgstr "" msgctxt "#32150" msgid "Scrapers" -msgstr "Scrapers" +msgstr "Raspadors" msgctxt "#32151" msgid "Options" @@ -179,11 +188,11 @@ msgstr "" msgctxt "#32154" msgid "Synchronised lyrics scrapers" -msgstr "Scrapers de lletres sincronitzats" +msgstr "Raspadors de lletres sincronitzats" msgctxt "#32155" msgid "Regular lyrics scrapers" -msgstr "Scrapers de lletres normals" +msgstr "Raspadors de lletres normals" msgctxt "#32156" msgid "Display lyrics automatically" @@ -211,7 +220,7 @@ msgstr "" msgctxt "#32162" msgid "Scraper test" -msgstr "" +msgstr "Prova del raspador" msgctxt "#32163" msgid "Testing: %s" @@ -219,11 +228,11 @@ msgstr "" msgctxt "#32164" msgid "All scrapers are working ok" -msgstr "" +msgstr "Tots els raspadors funcionen bé" msgctxt "#32165" msgid "Failed scrapers: %s" -msgstr "" +msgstr "Han fallat els raspadors: %s" msgctxt "#32166" msgid "Time offset for synchronised lyrics" @@ -237,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.cs_cz/strings.po b/script.cu.lrclyrics/resources/language/resource.language.cs_cz/strings.po index da6ab97b9..e775e7e3c 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.cs_cz/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.cs_cz/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.cy_gb/strings.po b/script.cu.lrclyrics/resources/language/resource.language.cy_gb/strings.po index 16668cd33..65994ba45 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.cy_gb/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.cy_gb/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.da_dk/strings.po b/script.cu.lrclyrics/resources/language/resource.language.da_dk/strings.po index dc7e8f518..09d32d3b8 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.da_dk/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.da_dk/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-27 10:14+0000\n" +"PO-Revision-Date: 2024-04-14 12:13+0000\n" "Last-Translator: Christian Gade \n" "Language-Team: Danish \n" "Language: da_dk\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.4.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Søg efter og gem txt sangtekster i sangmappen" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Hvis du skjuler musik-OSD og informationen Afspiller nu, vil baggrunden for sangtekstdialogen også blive skjult." +msgctxt "#32169" +msgid "Sources" +msgstr "Kilder" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.de_de/strings.po b/script.cu.lrclyrics/resources/language/resource.language.de_de/strings.po index fd64fbb0c..6ebc42164 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.de_de/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.de_de/strings.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-23 07:14+0000\n" -"Last-Translator: Kai Sommerfeld \n" +"PO-Revision-Date: 2024-05-31 05:32+0000\n" +"Last-Translator: Kai Sommerfeld \n" "Language-Team: German \n" "Language: de_de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -23,7 +23,7 @@ msgstr "CU LRC Lyrics" msgctxt "Addon Description" msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." -msgstr "CU LRC Lyrics ist ein Liedtext-Skript für Kodi, das sowohl normale als auch LRC-Liedtexte unterstützt. Das Skript kann nach synchronisierten / unsynchronisierten eingebetteten Liedtexten von Dateien oder Scrapern suchen. Es kann .lrc- / .txt-Liedtextdateien lesen, die im gleichen Pfad und mit dem gleichen Dateinamen mit einem .mp3 (oder eine anderen Musikart) gespeichert wurden." +msgstr "CU LRC Lyrics ist ein Liedtextskript für Kodi, das sowohl normale als auch LRC-Liedtexte unterstützt. Das Skript kann nach synchronisierten/unsynchronisierten eingebetteten Liedtexten von Dateien oder Scrapern suchen. Es kann .lrc-/.txt-Liedtextdateien lesen, die im gleichen Pfad und mit dem gleichen Dateinamen mit einem .mp3 (oder eine anderen Musikart) gespeichert wurden." # Script labels msgctxt "#32000" @@ -122,7 +122,7 @@ msgstr "-- Name des Unterordners" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "lrc-Liedtext in Titel-Ordner suchen und speichern" +msgstr "lrc-Liedtext in Titelordner suchen und speichern" msgctxt "#32114" msgid "%N - %A - %T" @@ -146,7 +146,15 @@ msgstr "Fehlersuche" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" -msgstr "txt-Liedtext in Titel-Ordner suchen und speichern" +msgstr "txt-Liedtext in Titelordner suchen und speichern" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "%A - %B - %N - %T" # empty strings from id 32120 to 32146 msgctxt "#32147" @@ -200,7 +208,7 @@ msgstr "Skript läuft bereits!" msgctxt "#32159" msgid "Enable debug logging" -msgstr "Fehlersuch-Protokollierung aktivieren" +msgstr "Debugprotokollierung aktivieren" msgctxt "#32160" msgid "Hide notifications" @@ -208,7 +216,7 @@ msgstr "Benachrichtigungen ausblenden" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "Chinesischen / koreanischen Text aus Liedtexten entfernen" +msgstr "Chinesischen/koreanischen Text aus Liedtexten entfernen" msgctxt "#32162" msgid "Scraper test" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Wenn Musik-OSD und „Gerade läuft“-Information ausgeblendet sind, wird der Hintergrund des Liedtextdialogs ebenfalls ausgeblendet." +msgctxt "#32169" +msgid "Sources" +msgstr "Quellen" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Liedtexte beim Hören von Internet Radiosendern" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "LrcLib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.el_gr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.el_gr/strings.po index d1897fe2e..c4d49cb3f 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.el_gr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.el_gr/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_AU/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_AU/strings.po new file mode 100644 index 000000000..c5c938d0f --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.en_AU/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_AU\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_NZ/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_NZ/strings.po new file mode 100644 index 000000000..103f36310 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.en_NZ/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_NZ\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_US/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_US/strings.po new file mode 100644 index 000000000..0087986c8 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.en_US/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_au/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_au/strings.po index 0440ccfbf..408d0b2a3 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.en_au/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.en_au/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_gb/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_gb/strings.po index b74e34d25..a71e75dca 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.en_gb/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.en_gb/strings.po @@ -149,7 +149,15 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" -# empty strings from id 32120 to 32146 +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32122 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -239,10 +247,18 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -250,7 +266,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -281,3 +297,11 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + +msgctxt "#32918" +msgid "RCLyricsBand" +msgstr "" + diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_nz/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_nz/strings.po index 834b87515..280ab33c8 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.en_nz/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.en_nz/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.en_us/strings.po b/script.cu.lrclyrics/resources/language/resource.language.en_us/strings.po index 64795fb2b..28d1ea122 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.en_us/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.en_us/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.eo/strings.po b/script.cu.lrclyrics/resources/language/resource.language.eo/strings.po index dcef74d01..326127f78 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.eo/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.eo/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.es_ar/strings.po b/script.cu.lrclyrics/resources/language/resource.language.es_ar/strings.po index 6606771f8..37f039456 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.es_ar/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.es_ar/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.es_es/strings.po b/script.cu.lrclyrics/resources/language/resource.language.es_es/strings.po index 972446302..38d0c5b54 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.es_es/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.es_es/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Spanish (http://www.transifex.com/projects/p/xbmc-addons/language/es/)\n" -"Language: es\n" +"PO-Revision-Date: 2024-05-31 05:32+0000\n" +"Last-Translator: Alfonso Cachero \n" +"Language-Team: Spanish (Spain) \n" +"Language: es_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -47,7 +48,7 @@ msgstr "Buscando letra...." msgctxt "#32005" msgid "Select lyrics:" -msgstr "Seleccione otra letra" +msgstr "Selecciona las letras:" msgctxt "#32006" msgid "Select different lyrics" @@ -69,15 +70,15 @@ msgstr "Adelantar: %ss" # Settings labels msgctxt "#32100" msgid "Search for and save lrc lyrics in separate lyrics folder" -msgstr "" +msgstr "Buscar y guardar letras lrc en una carpeta de letras separada" msgctxt "#32101" msgid "Search for and save txt lyrics in separate lyrics folder" -msgstr "" +msgstr "Buscar y guardar letras txt en una carpeta de letras separada" msgctxt "#32102" msgid "- Lyrics folder" -msgstr "Ubicación:" +msgstr "- Carpeta de letras" msgctxt "#32103" msgid "- Filename format" @@ -85,11 +86,11 @@ msgstr "Formato de archivo" msgctxt "#32104" msgid "%N %A - %T" -msgstr "" +msgstr "%N %A - %T" msgctxt "#32105" msgid "%A\\%T" -msgstr "" +msgstr "%A\\%T" msgctxt "#32106" msgid "Use only filename for search" @@ -101,31 +102,31 @@ msgstr "Formato para nombre de archivo al buscar" msgctxt "#32108" msgid "%A - %T" -msgstr "" +msgstr "%A - %T" msgctxt "#32109" msgid "%A\\%B\\%T" -msgstr "" +msgstr "%A\\%B\\%T" msgctxt "#32110" msgid "%A\\%B\\%N (-) %T" -msgstr "" +msgstr "%A\\%B\\%N (-) %T" msgctxt "#32111" msgid "- Lyrics are in a subfolder" -msgstr "" +msgstr "- Las letras están en una subcarpeta" msgctxt "#32112" msgid "-- Subfolder name" -msgstr "" +msgstr "-- Nombre de la subcarpeta" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "" +msgstr "Buscar y guardar letras lrc en la carpeta de la canción" msgctxt "#32114" msgid "%N - %A - %T" -msgstr "" +msgstr "%N - %A - %T" msgctxt "#32115" msgid "Display options" @@ -145,17 +146,25 @@ msgstr "Depuración" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" -msgstr "" +msgstr "Buscar y guardar letras txt en la carpeta de la canción" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "%A - %B - %N - %T" # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" -msgstr "" +msgstr "Buscar archivos de letras .txt locales" # empty strings from id 32120 to 32147 msgctxt "#32148" msgid "Hide dialog background, show lyrics only" -msgstr "" +msgstr "Ocultar el fondo del diálogo, mostrar sólo las letras" msgctxt "#32149" msgid "Expert" @@ -175,7 +184,7 @@ msgstr "Buscar letra incrustada" msgctxt "#32153" msgid "Search for local .lrc lyrics files" -msgstr "" +msgstr "Buscar archivos de letras .lrc locales" msgctxt "#32154" msgid "Synchronised lyrics scrapers" @@ -235,48 +244,69 @@ msgstr "Borrar letra" msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." -msgstr "" +msgstr "Si ocultas el OSD de música y la información de reproducción en curso, también se ocultará el fondo del diálogo de letras." + +msgctxt "#32169" +msgid "Sources" +msgstr "Fuentes" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Obtén las letras de las canciones cuando escuches emisoras de radio por Internet" # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" -msgstr "" +msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" -msgstr "" +msgstr "Music 163" msgctxt "#32905" msgid "Lyricsify" -msgstr "" +msgstr "Lyricsify" msgctxt "#32912" msgid "Genius" -msgstr "" +msgstr "Genius" msgctxt "#32913" msgid "Lyrics Mode" -msgstr "" +msgstr "Modo de letras" msgctxt "#32914" msgid "AZLyrics" -msgstr "" +msgstr "AZLyrics" msgctxt "#32915" msgid "Lyrics.com" -msgstr "" +msgstr "Lyrics.com" msgctxt "#32916" msgid "Dark Lyrics" -msgstr "" +msgstr "Dark Lyrics" + +msgctxt "#32917" +msgid "Super Music" +msgstr "Música Súper" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" # empty strings from id 32010 to 32100 # Settings labels diff --git a/script.cu.lrclyrics/resources/language/resource.language.es_mx/strings.po b/script.cu.lrclyrics/resources/language/resource.language.es_mx/strings.po index aa33ca580..5d0299fb8 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.es_mx/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.es_mx/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Buscar y guardar letras txt en la carpeta de la canción" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Si ocultas el OSD de música y la información de Reproduciendo ahora, el fondo del diálogo de las letras se ocultará también." +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "" msgctxt "#32902" msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.et_ee/strings.po b/script.cu.lrclyrics/resources/language/resource.language.et_ee/strings.po index 333fef1c6..454382e02 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.et_ee/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.et_ee/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-09-22 05:14+0000\n" +"PO-Revision-Date: 2024-04-10 14:13+0000\n" "Last-Translator: rimasx \n" "Language-Team: Estonian \n" "Language: et_ee\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.14.1\n" +"X-Generator: Weblate 5.4.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Otsi ja salvesta txt laulusõnad laulukausta" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Kui peidad muusika ekraanigraafika ja praegu esitatava teabe, peidetakse ka laulusõnade akna taust." +msgctxt "#32169" +msgid "Sources" +msgstr "Allikad" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Hangi laulusõnad netiraadiote kuulamisel" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.eu_es/strings.po b/script.cu.lrclyrics/resources/language/resource.language.eu_es/strings.po index d98d2eba9..14b42dfb9 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.eu_es/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.eu_es/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fa_AF/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fa_AF/strings.po new file mode 100644 index 000000000..6c8b1a9c2 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.fa_AF/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fa_AF\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fa_af/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fa_af/strings.po index d089488ff..1a63160d2 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fa_af/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fa_af/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fa_ir/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fa_ir/strings.po index 34c115df2..c240e66af 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fa_ir/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fa_ir/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fi_fi/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fi_fi/strings.po index 07fd9eb68..50e17d5a3 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fi_fi/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fi_fi/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-09-10 20:14+0000\n" +"PO-Revision-Date: 2024-04-22 10:13+0000\n" "Last-Translator: Oskari Lavinto \n" "Language-Team: Finnish \n" "Language: fi_fi\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.14\n" +"X-Generator: Weblate 5.4.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Käytä txt-sanoitusten tallennukseen ja etsintään kappaleiden kansioita" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -196,7 +204,7 @@ msgstr "Siivoa kappaleiden nimet (poista suluissa olevat tiedot)" msgctxt "#32158" msgid "Script is already running!" -msgstr "Skripti on jo käynnissä!" +msgstr "Komentosarja on jo käynnissä!" msgctxt "#32159" msgid "Enable debug logging" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Jos piilotat musiikin kuvaruutuvalikon ja \"Toistetaan parhaillaan\" -tiedot, piilotetaan myös sanoitusikkunan tausta." +msgctxt "#32169" +msgid "Sources" +msgstr "Lähteet" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Nouda sanoituksia kuunneltaessa nettiradioasemia" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fo_fo/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fo_fo/strings.po index c2427ac40..70688f1e2 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fo_fo/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fo_fo/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fr_CA/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fr_CA/strings.po new file mode 100644 index 000000000..5aea9d15e --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.fr_CA/strings.po @@ -0,0 +1,298 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fr_CA\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fr_ca/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fr_ca/strings.po index e2458678f..ba2ce86df 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fr_ca/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fr_ca/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.fr_fr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.fr_fr/strings.po index 1b1314d70..e37175a98 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.fr_fr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.fr_fr/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-10-02 16:15+0000\n" +"PO-Revision-Date: 2024-02-17 16:13+0000\n" "Last-Translator: skypichat \n" "Language-Team: French (France) \n" "Language: fr_fr\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.14.1\n" +"X-Generator: Weblate 5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -102,15 +102,15 @@ msgstr "Format de nom du fichier" msgctxt "#32108" msgid "%A - %T" -msgstr "Artiste - Chanson.ext" +msgstr "%A - %T" msgctxt "#32109" msgid "%A\\%B\\%T" -msgstr "Artiste\\Album\\Chanson.ext" +msgstr "%A\\%B\\%T" msgctxt "#32110" msgid "%A\\%B\\%N (-) %T" -msgstr "Artiste\\Album\\Piste Chanson.ext" +msgstr "%A\\%B\\%N (-) %T" msgctxt "#32111" msgid "- Lyrics are in a subfolder" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Rechercher et enregistrer les paroles txt dans le dossier de la chanson" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Si vous masquez l'OSD Musique et les informations de lecture en cours, l'arrière-plan de la boîte de dialogue des paroles sera également masqué." +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.gl_es/strings.po b/script.cu.lrclyrics/resources/language/resource.language.gl_es/strings.po index 9b1f05c2f..f8119497d 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.gl_es/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.gl_es/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.he_IL/strings.po b/script.cu.lrclyrics/resources/language/resource.language.he_IL/strings.po new file mode 100644 index 000000000..5ad8f9b99 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.he_IL/strings.po @@ -0,0 +1,299 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: 2024-04-14 12:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Hebrew (Israel) \n" +"Language: he_IL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && n % 10 == 0) ? 2 : 3));\n" +"X-Generator: Weblate 5.4.3\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "הפעל יומן רישום מורחב" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.he_il/strings.po b/script.cu.lrclyrics/resources/language/resource.language.he_il/strings.po index 02e020642..50d217543 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.he_il/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.he_il/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.hi_in/strings.po b/script.cu.lrclyrics/resources/language/resource.language.hi_in/strings.po index 4abc8bde1..136526e25 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.hi_in/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.hi_in/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.hr_hr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.hr_hr/strings.po index 20d9c6d67..264c4967c 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.hr_hr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.hr_hr/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Croatian (http://www.transifex.com/projects/p/xbmc-addons/language/hr/)\n" -"Language: hr\n" +"PO-Revision-Date: 2023-01-07 18:15+0000\n" +"Last-Translator: gogogogi \n" +"Language-Team: Croatian \n" +"Language: hr_hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.15\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -39,7 +40,7 @@ msgstr "Ugrađeno" msgctxt "#32003" msgid "Lyrics offset" -msgstr "" +msgstr "Pomak teksta pjesma" msgctxt "#32004" msgid "Searching lyrics..." @@ -47,33 +48,33 @@ msgstr "Pretraživanje teksta pjesama..." msgctxt "#32005" msgid "Select lyrics:" -msgstr "" +msgstr "Odaberi tekst pjesma:" msgctxt "#32006" msgid "Select different lyrics" -msgstr "" +msgstr "Odaberi drugačiji tekst pjesama" msgctxt "#32007" msgid "Manually sync lyrics" -msgstr "" +msgstr "Ručno uskladi tekst pjesme" msgctxt "#32008" msgid "Delayed by: %ss" -msgstr "" +msgstr "Odgodio: %ss" msgctxt "#32009" msgid "Ahead by: %ss" -msgstr "" +msgstr "Naprijed: %ss" # empty strings from id 32010 to 32100 # Settings labels msgctxt "#32100" msgid "Search for and save lrc lyrics in separate lyrics folder" -msgstr "" +msgstr "Pretraži i spremi lrc tekstove pjesama u zasebnoj mapi tekstova pjesama" msgctxt "#32101" msgid "Search for and save txt lyrics in separate lyrics folder" -msgstr "" +msgstr "Pretraži i sakrij txt tekstove pjesama u zasebnoj mapi teksta pjesama" msgctxt "#32102" msgid "- Lyrics folder" @@ -81,7 +82,7 @@ msgstr "- Mapa tekstova pjesama" msgctxt "#32103" msgid "- Filename format" -msgstr "- Spremi format" +msgstr "- Format naziva datoteka" msgctxt "#32104" msgid "%N %A - %T" @@ -113,53 +114,61 @@ msgstr "%A\\%B\\%N %T" msgctxt "#32111" msgid "- Lyrics are in a subfolder" -msgstr "" +msgstr "- Tekstovi pjesama su u podmapi" msgctxt "#32112" msgid "-- Subfolder name" -msgstr "" +msgstr "-- Naziv podmape" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "" +msgstr "Pretraži i spremi lrc tekstove pjesama u mapu pjesme" msgctxt "#32114" msgid "%N - %A - %T" -msgstr "" +msgstr "%N - %A - %T" msgctxt "#32115" msgid "Display options" -msgstr "" +msgstr "Mogućnosti prikaza" msgctxt "#32116" msgid "Search options" -msgstr "" +msgstr "Mogućnosti pretrage" msgctxt "#32117" msgid "Lyrics files" -msgstr "" +msgstr "Datoteke tekstova pjesama" msgctxt "#32118" msgid "Debugging" -msgstr "" +msgstr "Otklanjanje grešaka" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" +msgstr "Pretraži i spremi txt tekstove pjesama u mapu pjesme" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" msgstr "" # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" -msgstr "" +msgstr "Pretraži lokalne .txt datoteke tekstova pjesama" # empty strings from id 32120 to 32147 msgctxt "#32148" msgid "Hide dialog background, show lyrics only" -msgstr "" +msgstr "Sakrij pozadinu dijaloga, prikaži samo tekstove pjesama" msgctxt "#32149" msgid "Expert" -msgstr "" +msgstr "Stručno" msgctxt "#32150" msgid "Scrapers" @@ -171,11 +180,11 @@ msgstr "Mogućnosti" msgctxt "#32152" msgid "Search for embedded lyrics" -msgstr "Traži ugrađene tekstove pjesama" +msgstr "Pretraži ugrađene tekstove pjesama" msgctxt "#32153" msgid "Search for local .lrc lyrics files" -msgstr "" +msgstr "Pretraži lokalne .lrc datoteke tekstova pjesama" msgctxt "#32154" msgid "Synchronised lyrics scrapers" @@ -203,81 +212,102 @@ msgstr "Omogući zapisivanje otklanjanja grešaka" msgctxt "#32160" msgid "Hide notifications" -msgstr "" +msgstr "Sakrij obavijesti" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "" +msgstr "Ukloni kineski/korejski tekst iz teksta pjesama" msgctxt "#32162" msgid "Scraper test" -msgstr "" +msgstr "Test sakupljača" msgctxt "#32163" msgid "Testing: %s" -msgstr "" +msgstr "Testiram: %s" msgctxt "#32164" msgid "All scrapers are working ok" -msgstr "" +msgstr "Svi sakupljači rade uredu" msgctxt "#32165" msgid "Failed scrapers: %s" -msgstr "" +msgstr "Neuspjeli sakupljači: %s" msgctxt "#32166" msgid "Time offset for synchronised lyrics" -msgstr "" +msgstr "Vremenski pomak za usklađene tekstove pjesama" msgctxt "#32167" msgid "Delete lyrics" -msgstr "" +msgstr "Obriši tekstove pjesama" msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "Ako sakrijete Glazbeni OSD i Informacije trenutne reprodukcije, dijalog pozadine teksta pjesama biti će isto skriven." + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" msgstr "" # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" msgid "MusixMatch" -msgstr "" +msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" msgid "Music 163" -msgstr "" +msgstr "Music 163" msgctxt "#32905" msgid "Lyricsify" -msgstr "" +msgstr "Lyricsify" msgctxt "#32912" msgid "Genius" -msgstr "" +msgstr "Genius" msgctxt "#32913" msgid "Lyrics Mode" -msgstr "" +msgstr "Lyrics Mode" msgctxt "#32914" msgid "AZLyrics" -msgstr "" +msgstr "AZLyrics" msgctxt "#32915" msgid "Lyrics.com" -msgstr "" +msgstr "Lyrics.com" msgctxt "#32916" msgid "Dark Lyrics" +msgstr "Dark Lyrics" + +msgctxt "#32917" +msgid "Super Music" msgstr "" +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.hu_hu/strings.po b/script.cu.lrclyrics/resources/language/resource.language.hu_hu/strings.po index bb725980a..21943abbf 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.hu_hu/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.hu_hu/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.hy_am/strings.po b/script.cu.lrclyrics/resources/language/resource.language.hy_am/strings.po index 6bd6cb079..867ebc9a0 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.hy_am/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.hy_am/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.id_id/strings.po b/script.cu.lrclyrics/resources/language/resource.language.id_id/strings.po index 2cc030004..becd0bf22 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.id_id/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.id_id/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Cari dan simpan lirik txt di folder lagu" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Jika Anda menyembunyikan OSD Musik dan info Sedang Diputar, latar belakang dialog lirik juga akan disembunyikan." +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Lirik Gelap" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.is_is/strings.po b/script.cu.lrclyrics/resources/language/resource.language.is_is/strings.po index c0f57fb51..6ad854e40 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.is_is/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.is_is/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.it_it/strings.po b/script.cu.lrclyrics/resources/language/resource.language.it_it/strings.po index fb31d926e..4b7033f17 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.it_it/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.it_it/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-08-15 22:34+0000\n" +"PO-Revision-Date: 2024-05-30 04:42+0000\n" "Last-Translator: Massimo Pissarello \n" "Language-Team: Italian \n" "Language: it_it\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -40,11 +40,11 @@ msgstr "Incorporato" msgctxt "#32003" msgid "Lyrics offset" -msgstr "Compensazione testo" +msgstr "Sfasamento testo" msgctxt "#32004" msgid "Searching lyrics..." -msgstr "Ricerca testi..." +msgstr "Ricerca testi in corso..." msgctxt "#32005" msgid "Select lyrics:" @@ -134,7 +134,7 @@ msgstr "Opzioni visualizzazione" msgctxt "#32116" msgid "Search options" -msgstr "Opzioni di ricerca" +msgstr "Opzioni ricerca" msgctxt "#32117" msgid "Lyrics files" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Cerca e salva i testi txt nella cartella dei brani" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "%A - %B - %N - %T" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -180,11 +188,11 @@ msgstr "Cerca file di testo .lrc locali" msgctxt "#32154" msgid "Synchronised lyrics scrapers" -msgstr "Scraper testi sincronizzato" +msgstr "Scraper testi sincronizzati" msgctxt "#32155" msgid "Regular lyrics scrapers" -msgstr "Scraper testi regolare" +msgstr "Scraper testi regolari" msgctxt "#32156" msgid "Display lyrics automatically" @@ -208,23 +216,23 @@ msgstr "Nascondi notifiche" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "Rimuovi testo Cinese/Coreano dai testi" +msgstr "Rimuovi parti in Cinese/Coreano dai testi" msgctxt "#32162" msgid "Scraper test" -msgstr "Testa scraper" +msgstr "Prova scraper" msgctxt "#32163" msgid "Testing: %s" -msgstr "Testando: %s" +msgstr "Provando: %s" msgctxt "#32164" msgid "All scrapers are working ok" -msgstr "Tutti gli scraper stanno funzionando" +msgstr "Tutti gli scraper funzionano correttamente" msgctxt "#32165" msgid "Failed scrapers: %s" -msgstr "Scraper falliti: %s" +msgstr "Scraper non riusciti: %s" msgctxt "#32166" msgid "Time offset for synchronised lyrics" @@ -232,24 +240,32 @@ msgstr "Tempo di sfasamento sincronizzazione testi" msgctxt "#32167" msgid "Delete lyrics" -msgstr "Elimina i testi" +msgstr "Elimina testi" msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." -msgstr "Se nascondi l'OSD della musica e le informazioni in riproduzione, anche lo sfondo della finestra di dialogo dei testi verrà nascosto." +msgstr "Se nascondi l'OSD della musica e le informazioni sul brano in riproduzione, anche lo sfondo della finestra di dialogo dei testi verrà nascosto." + +msgctxt "#32169" +msgid "Sources" +msgstr "Sorgenti" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Ottieni testi quando ascolti stazioni radio internet" # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ja_jp/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ja_jp/strings.po index ec6801f74..288c6e457 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ja_jp/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ja_jp/strings.po @@ -69,11 +69,11 @@ msgstr "" # Settings labels msgctxt "#32100" msgid "Search for and save lrc lyrics in separate lyrics folder" -msgstr "" +msgstr "lrc歌詞を別の歌詞フォルダーに保存" msgctxt "#32101" msgid "Search for and save txt lyrics in separate lyrics folder" -msgstr "" +msgstr "txt歌詞を別の歌詞フォルダーに保存" msgctxt "#32102" msgid "- Lyrics folder" @@ -113,15 +113,15 @@ msgstr "" msgctxt "#32111" msgid "- Lyrics are in a subfolder" -msgstr "" +msgstr "サブフォルダーに保存" msgctxt "#32112" msgid "-- Subfolder name" -msgstr "" +msgstr "サブフォルダー名" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "" +msgstr "lrc歌詞を楽曲のフォルダーに保存" msgctxt "#32114" msgid "%N - %A - %T" @@ -145,12 +145,20 @@ msgstr "デバッグ" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" +msgstr "txt歌詞を楽曲のフォルダーに保存" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" msgstr "" # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" -msgstr "" +msgstr "ローカルに保存されたtxt歌詞を検索する" # empty strings from id 32120 to 32147 msgctxt "#32148" @@ -175,7 +183,7 @@ msgstr "楽曲に埋め込まれた歌詞を検索する" msgctxt "#32153" msgid "Search for local .lrc lyrics files" -msgstr "" +msgstr "ローカルに保存されたlrc歌詞を検索する" msgctxt "#32154" msgid "Synchronised lyrics scrapers" @@ -207,7 +215,7 @@ msgstr "通知を非表示" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "漢字テキストを除外" +msgstr "漢字/ハングル文字を除外" msgctxt "#32162" msgid "Scraper test" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.kn_in/strings.po b/script.cu.lrclyrics/resources/language/resource.language.kn_in/strings.po index 198f04aba..614816d12 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.kn_in/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.kn_in/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ko_kr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ko_kr/strings.po index 3a373faf9..e9c3beac6 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ko_kr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ko_kr/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-25 09:14+0000\n" +"PO-Revision-Date: 2024-05-30 04:42+0000\n" "Last-Translator: Minho Park \n" "Language-Team: Korean \n" "Language: ko_kr\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "노래 폴더에서 txt 가사 검색 및 저장" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "%A - %B - %N - %T" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "음악 OSD 및 지금 재생 중 정보를 숨기면, 가사 대화 상자의 배경도 숨겨집니다." +msgctxt "#32169" +msgid "Sources" +msgstr "출처" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "인터넷 라디오 방송을 들을 때 가사 받기" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "곰오디오" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "어두운 가사" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "곰오디오" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.lt_lt/strings.po b/script.cu.lrclyrics/resources/language/resource.language.lt_lt/strings.po index 4b22e6aab..3097e09e2 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.lt_lt/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.lt_lt/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.lv_lv/strings.po b/script.cu.lrclyrics/resources/language/resource.language.lv_lv/strings.po index d82257df0..85f870de6 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.lv_lv/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.lv_lv/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.mi/strings.po b/script.cu.lrclyrics/resources/language/resource.language.mi/strings.po index 267beacef..b8d94b517 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.mi/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.mi/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.mk_mk/strings.po b/script.cu.lrclyrics/resources/language/resource.language.mk_mk/strings.po index 0185f9e95..1c72d8d3e 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.mk_mk/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.mk_mk/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ml_in/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ml_in/strings.po index 4055e6a99..708675b48 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ml_in/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ml_in/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.mn_mn/strings.po b/script.cu.lrclyrics/resources/language/resource.language.mn_mn/strings.po index 696c76685..64f1587ff 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.mn_mn/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.mn_mn/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ms_my/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ms_my/strings.po index 503f28137..c47503bd7 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ms_my/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ms_my/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,3 +294,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.mt_mt/strings.po b/script.cu.lrclyrics/resources/language/resource.language.mt_mt/strings.po index 6c754a3ac..b80a5f562 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.mt_mt/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.mt_mt/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,3 +294,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.my_mm/strings.po b/script.cu.lrclyrics/resources/language/resource.language.my_mm/strings.po index 5461d1ebb..9b7e24fb7 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.my_mm/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.my_mm/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.nb_no/strings.po b/script.cu.lrclyrics/resources/language/resource.language.nb_no/strings.po index aaaf9c5fd..c7629c1dc 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.nb_no/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.nb_no/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.nl_nl/strings.po b/script.cu.lrclyrics/resources/language/resource.language.nl_nl/strings.po index 3c03e3551..74c6c0dea 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.nl_nl/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.nl_nl/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Zoek naar en bewaar txt-teksten in de songmap" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.oc_fr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.oc_fr/strings.po index 935e66586..789ed36d5 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.oc_fr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.oc_fr/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.os_os/strings.po b/script.cu.lrclyrics/resources/language/resource.language.os_os/strings.po index 00148c024..90947fb11 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.os_os/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.os_os/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.pl_pl/strings.po b/script.cu.lrclyrics/resources/language/resource.language.pl_pl/strings.po index e71d7608a..6b4d83286 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.pl_pl/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.pl_pl/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-23 07:14+0000\n" +"PO-Revision-Date: 2024-05-13 07:14+0000\n" "Last-Translator: Marek Adamski \n" "Language-Team: Polish \n" "Language: pl_pl\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.5.4\n" # Script labels msgctxt "Addon Summary" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Wyszukaj i zapisz teksty utworów w formacie TXT w folderze z piosenkami" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Jeśli ukryjesz OSD muzyki oraz informacje o bieżącym odtwarzaniu, tło okienka tekstów będzie również ukryte." +msgctxt "#32169" +msgid "Sources" +msgstr "Źródła" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Pobieraj teksty piosenek podczas słuchania internetowych stacji radiowych" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.pt_PT/strings.po b/script.cu.lrclyrics/resources/language/resource.language.pt_PT/strings.po new file mode 100644 index 000000000..823b12777 --- /dev/null +++ b/script.cu.lrclyrics/resources/language/resource.language.pt_PT/strings.po @@ -0,0 +1,299 @@ +# Kodi Media Center language file +# Addon Name: CU LRC Lyrics +# Addon id: script.cu.lrclyrics +# Addon Provider: Taxigps|ronie +msgid "" +msgstr "" +"Project-Id-Version: Kodi add-ons\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: 2024-04-14 12:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Portuguese (Portugal) \n" +"Language: pt_PT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.4.3\n" + +msgctxt "Addon Summary" +msgid "CU LRC Lyrics" +msgstr "" + +msgctxt "Addon Description" +msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." +msgstr "" + +# Script labels +msgctxt "#32000" +msgid "File" +msgstr "" + +msgctxt "#32001" +msgid "No lyrics found!" +msgstr "" + +msgctxt "#32002" +msgid "Embedded" +msgstr "" + +msgctxt "#32003" +msgid "Lyrics offset" +msgstr "" + +msgctxt "#32004" +msgid "Searching lyrics..." +msgstr "" + +msgctxt "#32005" +msgid "Select lyrics:" +msgstr "" + +msgctxt "#32006" +msgid "Select different lyrics" +msgstr "" + +msgctxt "#32007" +msgid "Manually sync lyrics" +msgstr "" + +msgctxt "#32008" +msgid "Delayed by: %ss" +msgstr "" + +msgctxt "#32009" +msgid "Ahead by: %ss" +msgstr "" + +# empty strings from id 32010 to 32100 +# Settings labels +msgctxt "#32100" +msgid "Search for and save lrc lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32101" +msgid "Search for and save txt lyrics in separate lyrics folder" +msgstr "" + +msgctxt "#32102" +msgid "- Lyrics folder" +msgstr "" + +msgctxt "#32103" +msgid "- Filename format" +msgstr "" + +msgctxt "#32104" +msgid "%N %A - %T" +msgstr "" + +msgctxt "#32105" +msgid "%A\\%T" +msgstr "" + +msgctxt "#32106" +msgid "Use only filename for search" +msgstr "" + +msgctxt "#32107" +msgid "Filename format" +msgstr "" + +msgctxt "#32108" +msgid "%A - %T" +msgstr "" + +msgctxt "#32109" +msgid "%A\\%B\\%T" +msgstr "" + +msgctxt "#32110" +msgid "%A\\%B\\%N (-) %T" +msgstr "" + +msgctxt "#32111" +msgid "- Lyrics are in a subfolder" +msgstr "" + +msgctxt "#32112" +msgid "-- Subfolder name" +msgstr "" + +msgctxt "#32113" +msgid "Search for and save lrc lyrics in song folder" +msgstr "" + +msgctxt "#32114" +msgid "%N - %A - %T" +msgstr "" + +msgctxt "#32115" +msgid "Display options" +msgstr "" + +msgctxt "#32116" +msgid "Search options" +msgstr "" + +msgctxt "#32117" +msgid "Lyrics files" +msgstr "" + +msgctxt "#32118" +msgid "Debugging" +msgstr "" + +msgctxt "#32119" +msgid "Search for and save txt lyrics in song folder" +msgstr "" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + +# empty strings from id 32120 to 32146 +msgctxt "#32147" +msgid "Search for local .txt lyrics files" +msgstr "" + +msgctxt "#32148" +msgid "Hide dialog background, show lyrics only" +msgstr "" + +msgctxt "#32149" +msgid "Expert" +msgstr "" + +msgctxt "#32150" +msgid "Scrapers" +msgstr "" + +msgctxt "#32151" +msgid "Options" +msgstr "" + +msgctxt "#32152" +msgid "Search for embedded lyrics" +msgstr "" + +msgctxt "#32153" +msgid "Search for local .lrc lyrics files" +msgstr "" + +msgctxt "#32154" +msgid "Synchronised lyrics scrapers" +msgstr "" + +msgctxt "#32155" +msgid "Regular lyrics scrapers" +msgstr "" + +msgctxt "#32156" +msgid "Display lyrics automatically" +msgstr "" + +msgctxt "#32157" +msgid "Clean song title (remove part in brackets)" +msgstr "" + +msgctxt "#32158" +msgid "Script is already running!" +msgstr "" + +msgctxt "#32159" +msgid "Enable debug logging" +msgstr "Ativar registo de depuração" + +msgctxt "#32160" +msgid "Hide notifications" +msgstr "" + +msgctxt "#32161" +msgid "Remove Chinese/Korean text from lyrics" +msgstr "" + +msgctxt "#32162" +msgid "Scraper test" +msgstr "" + +msgctxt "#32163" +msgid "Testing: %s" +msgstr "" + +msgctxt "#32164" +msgid "All scrapers are working ok" +msgstr "" + +msgctxt "#32165" +msgid "Failed scrapers: %s" +msgstr "" + +msgctxt "#32166" +msgid "Time offset for synchronised lyrics" +msgstr "" + +msgctxt "#32167" +msgid "Delete lyrics" +msgstr "" + +msgctxt "#32168" +msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "" + +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + +# Scrapers - do not translate! +msgctxt "#32901" +msgid "Lrclib" +msgstr "" + +msgctxt "#32902" +msgid "MusixMatch" +msgstr "" + +msgctxt "#32903" +msgid "Megalobiz" +msgstr "" + +msgctxt "#32904" +msgid "Music 163" +msgstr "" + +msgctxt "#32905" +msgid "Lyricsify" +msgstr "" + +msgctxt "#32912" +msgid "Genius" +msgstr "" + +msgctxt "#32913" +msgid "Lyrics Mode" +msgstr "" + +msgctxt "#32914" +msgid "AZLyrics" +msgstr "" + +msgctxt "#32915" +msgid "Lyrics.com" +msgstr "" + +msgctxt "#32916" +msgid "Dark Lyrics" +msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.pt_br/strings.po b/script.cu.lrclyrics/resources/language/resource.language.pt_br/strings.po index 22dce93d1..6a8fa5f25 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.pt_br/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.pt_br/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/xbmc-addons/language/pt_BR/)\n" -"Language: pt_BR\n" +"PO-Revision-Date: 2024-05-17 05:26+0000\n" +"Last-Translator: Havok Dan \n" +"Language-Team: Portuguese (Brazil) \n" +"Language: pt_br\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -39,7 +40,7 @@ msgstr "Embutida" msgctxt "#32003" msgid "Lyrics offset" -msgstr "" +msgstr "Deslocamento de letras" msgctxt "#32004" msgid "Searching lyrics..." @@ -47,33 +48,33 @@ msgstr "Procurando letras da música..." msgctxt "#32005" msgid "Select lyrics:" -msgstr "" +msgstr "Selecionar a letra:" msgctxt "#32006" msgid "Select different lyrics" -msgstr "" +msgstr "Selecione letras diferentes" msgctxt "#32007" msgid "Manually sync lyrics" -msgstr "" +msgstr "Sincronizar letras manualmente" msgctxt "#32008" msgid "Delayed by: %ss" -msgstr "" +msgstr "Atrasado por: %ss" msgctxt "#32009" msgid "Ahead by: %ss" -msgstr "" +msgstr "Adiantar por: %ss" # empty strings from id 32010 to 32100 # Settings labels msgctxt "#32100" msgid "Search for and save lrc lyrics in separate lyrics folder" -msgstr "" +msgstr "Procure e salve as letras lrc em uma pasta de letras separada" msgctxt "#32101" msgid "Search for and save txt lyrics in separate lyrics folder" -msgstr "" +msgstr "Pesquise e salve letras em txt em uma pasta de letras separada" msgctxt "#32102" msgid "- Lyrics folder" @@ -113,53 +114,63 @@ msgstr "%A\\%B\\%N %T" msgctxt "#32111" msgid "- Lyrics are in a subfolder" -msgstr "" +msgstr "- As letras estão em uma subpasta" msgctxt "#32112" msgid "-- Subfolder name" -msgstr "" +msgstr "-- Nome da subpasta" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "" +msgstr "Procure e salve as letras lrc na pasta de músicas" +#, fuzzy msgctxt "#32114" msgid "%N - %A - %T" -msgstr "" +msgstr "%N - %A - %T" msgctxt "#32115" msgid "Display options" -msgstr "" +msgstr "Opções de exibição" msgctxt "#32116" msgid "Search options" -msgstr "" +msgstr "Opções de busca" msgctxt "#32117" msgid "Lyrics files" -msgstr "" +msgstr "Arquivos de letras" msgctxt "#32118" msgid "Debugging" -msgstr "" +msgstr "Depurar" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" +msgstr "Procure e salve letras em txt na pasta de músicas" + +#, fuzzy +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "%N. %A - %T" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" msgstr "" # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" -msgstr "" +msgstr "Pesquise arquivos de letras .txt locais" # empty strings from id 32120 to 32147 msgctxt "#32148" msgid "Hide dialog background, show lyrics only" -msgstr "" +msgstr "Ocultar o fundo da caixa de diálogo, mostrar apenas as letras" msgctxt "#32149" msgid "Expert" -msgstr "" +msgstr "Especialista" msgctxt "#32150" msgid "Scrapers" @@ -175,7 +186,7 @@ msgstr "Procurar por letras embutidas" msgctxt "#32153" msgid "Search for local .lrc lyrics files" -msgstr "" +msgstr "Pesquise arquivos de letras .lrc locais" msgctxt "#32154" msgid "Synchronised lyrics scrapers" @@ -207,76 +218,98 @@ msgstr "Ocultar notificações" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "" +msgstr "Remover texto em chinês/coreano das letras" msgctxt "#32162" msgid "Scraper test" -msgstr "" +msgstr "Testar pesquisa" msgctxt "#32163" msgid "Testing: %s" -msgstr "" +msgstr "Testando: %s" msgctxt "#32164" msgid "All scrapers are working ok" -msgstr "" +msgstr "Todos os sites estão funcionando bem" msgctxt "#32165" msgid "Failed scrapers: %s" -msgstr "" +msgstr "Sites com falha: %s" msgctxt "#32166" msgid "Time offset for synchronised lyrics" -msgstr "" +msgstr "Deslocamento de tempo para letras sincronizadas" msgctxt "#32167" msgid "Delete lyrics" -msgstr "" +msgstr "Excluir letras" msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." -msgstr "" +msgstr "Se você ocultar o OSD da música e as informações do Reproduzindo Agora, o plano de fundo da caixa de diálogo da letra também ficará oculto." + +msgctxt "#32169" +msgid "Sources" +msgstr "Fontes" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Obtenha letras ao ouvir estações de rádio da Internet" # Scrapers - do not translate! +#, fuzzy msgctxt "#32901" -msgid "MiniLyrics" -msgstr "" +msgid "Lrclib" +msgstr "Lrclib" +#, fuzzy msgctxt "#32902" msgid "MusixMatch" -msgstr "" +msgstr "MusixMatch" +#, fuzzy msgctxt "#32903" -msgid "GomAudio" -msgstr "" +msgid "Megalobiz" +msgstr "Megalobiz" +#, fuzzy msgctxt "#32904" msgid "Music 163" -msgstr "" +msgstr "Music 163" +#, fuzzy msgctxt "#32905" msgid "Lyricsify" -msgstr "" +msgstr "Lyricsify" +#, fuzzy msgctxt "#32912" msgid "Genius" -msgstr "" +msgstr "Genius" +#, fuzzy msgctxt "#32913" msgid "Lyrics Mode" -msgstr "" +msgstr "Lyrics Mode" +#, fuzzy msgctxt "#32914" msgid "AZLyrics" -msgstr "" +msgstr "AZLyrics" +#, fuzzy msgctxt "#32915" msgid "Lyrics.com" -msgstr "" +msgstr "Lyrics.com" +#, fuzzy msgctxt "#32916" msgid "Dark Lyrics" -msgstr "" +msgstr "Dark Lyrics" + +msgctxt "#32917" +msgid "Super Music" +msgstr "Supermúsica" # empty strings from id 32010 to 32100 # Settings labels diff --git a/script.cu.lrclyrics/resources/language/resource.language.pt_pt/strings.po b/script.cu.lrclyrics/resources/language/resource.language.pt_pt/strings.po index f7ac6bd73..d102dce12 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.pt_pt/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.pt_pt/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ro_ro/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ro_ro/strings.po index e451effec..b08a20622 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ro_ro/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ro_ro/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ru_ru/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ru_ru/strings.po index 570f9292f..afb25e0df 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ru_ru/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ru_ru/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-11-05 15:15+0000\n" +"PO-Revision-Date: 2024-04-30 17:26+0000\n" "Last-Translator: vgbsd \n" "Language-Team: Russian \n" "Language: ru_ru\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.14.2\n" +"X-Generator: Weblate 5.5.2\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "Поиск и сохранение текстов в формате txt в папке с песнями" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "Если скрыть экранное меню музыки и информацию \"Сейчас играет\", фон диалогового окна с текстом песни также будет скрыт." +msgctxt "#32169" +msgid "Sources" +msgstr "Источники" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "Получать тексты песен при прослушивании интернет-радиостанций" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "Lrclib" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "Megalobiz" msgctxt "#32904" msgid "Music 163" @@ -278,3 +294,16 @@ msgstr "Lyrics.com" msgctxt "#32916" msgid "Dark Lyrics" msgstr "Тёмный текст песен" + +msgctxt "#32917" +msgid "Super Music" +msgstr "Super Music" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" diff --git a/script.cu.lrclyrics/resources/language/resource.language.si_lk/strings.po b/script.cu.lrclyrics/resources/language/resource.language.si_lk/strings.po index e535b9dd9..1918cc2c0 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.si_lk/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.si_lk/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sk_sk/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sk_sk/strings.po index 582806eef..9986b47a0 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sk_sk/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sk_sk/strings.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Kodi Translation Team\n" -"Language-Team: Slovak (http://www.transifex.com/projects/p/xbmc-addons/language/sk/)\n" -"Language: sk\n" +"PO-Revision-Date: 2024-04-14 12:13+0000\n" +"Last-Translator: Christian Gade \n" +"Language-Team: Slovak \n" +"Language: sk_sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 5.4.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -22,7 +23,7 @@ msgstr "CU LRC Lyrics" msgctxt "Addon Description" msgid "CU LRC Lyrics is a lyrics script for Kodi. It supports regular as well as LRC Lyrics. The script can search synchronized/unsynchronized lyrics embedded, from file or by scrapers. It can read .lrc/.txt lyrics file saved at the same path by same file name with .mp3(or other type of music)." -msgstr "CU LRC Lyrics je doplnok poskytujúci texty skladieb pre Kodi. Podporuje bežné ako aj LRC formáty. Doplnok môže vyhľadávať synchronizované/nesynchronizované Texty skladieb vložené, v samostatnom súbore alebo cez sťahovače. Dokáže čítať .lrc/.txt súbory v rovnakom priečinku a s rovnakým názvom ako .mp3 súbor (alebo iný hudobný súbor)." +msgstr "CU LRC Lyrics je doplnok poskytujúci texty piesní pre Kodi. Podporuje bežné ako aj LRC formáty. Doplnok môže vyhľadávať synchronizované/nesynchronizované texty skladieb vložené v súbore s hudbou, v samostatnom súbore alebo ich stiahnuť z internetu. Dokáže čítať súbory vo formáte LRC/TXT v rovnakom priečinku a s rovnakým názvom ako .mp3 súbor (alebo iný hudobný súbor)." # Script labels msgctxt "#32000" @@ -31,7 +32,7 @@ msgstr "Súbor" msgctxt "#32001" msgid "No lyrics found!" -msgstr "Texty skladieb nenájdené!" +msgstr "Text piesne nebol nájdený!" msgctxt "#32002" msgid "Embedded" @@ -39,61 +40,61 @@ msgstr "Vložené" msgctxt "#32003" msgid "Lyrics offset" -msgstr "" +msgstr "Posun textu piesne" msgctxt "#32004" msgid "Searching lyrics..." -msgstr "" +msgstr "Vyhľadávam text piesne..." msgctxt "#32005" msgid "Select lyrics:" -msgstr "" +msgstr "Vyberte text piesne:" msgctxt "#32006" msgid "Select different lyrics" -msgstr "" +msgstr "Vyberte iný text piesne" msgctxt "#32007" msgid "Manually sync lyrics" -msgstr "" +msgstr "Manuálne synchronizovať text" msgctxt "#32008" msgid "Delayed by: %ss" -msgstr "" +msgstr "Oneskorený o: %ss" msgctxt "#32009" msgid "Ahead by: %ss" -msgstr "" +msgstr "Vpred o %ss" # empty strings from id 32010 to 32100 # Settings labels msgctxt "#32100" msgid "Search for and save lrc lyrics in separate lyrics folder" -msgstr "" +msgstr "Hľadať a ukladať texty vo formáte LRC do samostatného priečinka s textami" msgctxt "#32101" msgid "Search for and save txt lyrics in separate lyrics folder" -msgstr "" +msgstr "Hľadať a ukladať texty vo formáte TXT do samostatného priečinku s textami" msgctxt "#32102" msgid "- Lyrics folder" -msgstr "Uložiť priečinok" +msgstr "- Priečinok s textami" msgctxt "#32103" msgid "- Filename format" -msgstr "" +msgstr "- Formát názvu súboru" msgctxt "#32104" msgid "%N %A - %T" -msgstr "" +msgstr "%N %A - %T" msgctxt "#32105" msgid "%A\\%T" -msgstr "" +msgstr "%A\\%T" msgctxt "#32106" msgid "Use only filename for search" -msgstr "Použiť len názov súboru pre vyhľadávanie" +msgstr "Pre vyhľadávanie používať len názov súboru" msgctxt "#32107" msgid "Filename format" @@ -113,53 +114,61 @@ msgstr "%A\\%B\\%N %T" msgctxt "#32111" msgid "- Lyrics are in a subfolder" -msgstr "" +msgstr "- Texty piesní sú v podpriečinku" msgctxt "#32112" msgid "-- Subfolder name" -msgstr "" +msgstr "-- Názov podpriečinku" msgctxt "#32113" msgid "Search for and save lrc lyrics in song folder" -msgstr "" +msgstr "Hľadať a ukladať texty vo formáte LRC do priečinka s piesňami" msgctxt "#32114" msgid "%N - %A - %T" -msgstr "" +msgstr "%N - %A - %T" msgctxt "#32115" msgid "Display options" -msgstr "" +msgstr "Možnosti zobrazenia" msgctxt "#32116" msgid "Search options" -msgstr "" +msgstr "Možnosti vyhľadávania" msgctxt "#32117" msgid "Lyrics files" -msgstr "" +msgstr "Súbory s textami" msgctxt "#32118" msgid "Debugging" -msgstr "" +msgstr "Ladenie" msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" +msgstr "Hľadať a ukladať texty vo formáte TXT do priečinka s piesňami" + +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" msgstr "" # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" -msgstr "" +msgstr "Hľadať lokálne súbory vo formáte TXT" # empty strings from id 32120 to 32147 msgctxt "#32148" msgid "Hide dialog background, show lyrics only" -msgstr "" +msgstr "Skryť dialógové okno, zobraziť iba texty piesní" msgctxt "#32149" msgid "Expert" -msgstr "" +msgstr "Expert" msgctxt "#32150" msgid "Scrapers" @@ -171,113 +180,134 @@ msgstr "Možnosti" msgctxt "#32152" msgid "Search for embedded lyrics" -msgstr "Hľadať vložené Texty skladieb" +msgstr "Hľadať vložené texty piesní" msgctxt "#32153" msgid "Search for local .lrc lyrics files" -msgstr "" +msgstr "Hľadať lokálne súbory vo formáte LRC" msgctxt "#32154" msgid "Synchronised lyrics scrapers" -msgstr "Sťahovače synchronizovaných textov skladieb" +msgstr "Sťahovače synchronizovaných textov piesní" msgctxt "#32155" msgid "Regular lyrics scrapers" -msgstr "Sťahovače bežných textov skladieb" +msgstr "Sťahovače bežných textov piesní" msgctxt "#32156" msgid "Display lyrics automatically" -msgstr "" +msgstr "Zobraziť text piesne automaticky" msgctxt "#32157" msgid "Clean song title (remove part in brackets)" -msgstr "" +msgstr "Vyčistiť názov piesne (odstrániť časti v zátvorkách)" msgctxt "#32158" msgid "Script is already running!" -msgstr "" +msgstr "Skript je už spustený!" msgctxt "#32159" msgid "Enable debug logging" -msgstr "Začať ukladať informácie / debug log" +msgstr "Povoliť ukladanie ladiacich informácií" msgctxt "#32160" msgid "Hide notifications" -msgstr "" +msgstr "Skryť oznámenia" msgctxt "#32161" msgid "Remove Chinese/Korean text from lyrics" -msgstr "" +msgstr "Odstrániť čínske/kórejské texty z textov piesní" msgctxt "#32162" msgid "Scraper test" -msgstr "" +msgstr "Test sťahovačov" msgctxt "#32163" msgid "Testing: %s" -msgstr "" +msgstr "Testujem: %s" msgctxt "#32164" msgid "All scrapers are working ok" -msgstr "" +msgstr "Všetky sťahovače fungujú správne" msgctxt "#32165" msgid "Failed scrapers: %s" -msgstr "" +msgstr "Nefungujúce sťahovače: %s" msgctxt "#32166" msgid "Time offset for synchronised lyrics" -msgstr "" +msgstr "Časový posun pre synchronizované texty" msgctxt "#32167" msgid "Delete lyrics" -msgstr "" +msgstr "Odstrániť text piesne" msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." +msgstr "Ak skryjete Hudobný OSD a informácie o aktuálne prehrávanej skladbe, okno s textom piesne sa skryje tiež." + +msgctxt "#32169" +msgid "Sources" +msgstr "Zdroje" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" msgstr "" # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" msgid "MusixMatch" -msgstr "" +msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" msgid "Music 163" -msgstr "" +msgstr "Music 163" msgctxt "#32905" msgid "Lyricsify" -msgstr "" +msgstr "Lyricsify" msgctxt "#32912" msgid "Genius" -msgstr "" +msgstr "Genius" msgctxt "#32913" msgid "Lyrics Mode" -msgstr "" +msgstr "Lyrics Mode" msgctxt "#32914" msgid "AZLyrics" -msgstr "" +msgstr "AZLyrics" msgctxt "#32915" msgid "Lyrics.com" -msgstr "" +msgstr "Lyrics.com" msgctxt "#32916" msgid "Dark Lyrics" +msgstr "Dark Lyrics" + +msgctxt "#32917" +msgid "Super Music" msgstr "" +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sl_si/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sl_si/strings.po index dedbb18d9..22762d4d0 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sl_si/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sl_si/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sq_al/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sq_al/strings.po index 9800776ec..6bb2fdb10 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sq_al/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sq_al/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sr_rs/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sr_rs/strings.po index ff98e2f8d..f8bc430d8 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sr_rs/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sr_rs/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sr_rs@latin/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sr_rs@latin/strings.po index 9afac98a2..072e8ae78 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sr_rs@latin/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sr_rs@latin/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.sv_se/strings.po b/script.cu.lrclyrics/resources/language/resource.language.sv_se/strings.po index 672ceb93e..3210e8368 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.sv_se/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.sv_se/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,6 +294,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.szl/strings.po b/script.cu.lrclyrics/resources/language/resource.language.szl/strings.po index 46fb1bfdf..c2289b75a 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.szl/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.szl/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.ta_in/strings.po b/script.cu.lrclyrics/resources/language/resource.language.ta_in/strings.po index d1f3bbf07..3a29a584e 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.ta_in/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.ta_in/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.te_in/strings.po b/script.cu.lrclyrics/resources/language/resource.language.te_in/strings.po index 919d31eca..cb16e7bd4 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.te_in/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.te_in/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.tg_tj/strings.po b/script.cu.lrclyrics/resources/language/resource.language.tg_tj/strings.po index 51954cdf4..7a224ba81 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.tg_tj/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.tg_tj/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.th_th/strings.po b/script.cu.lrclyrics/resources/language/resource.language.th_th/strings.po index ec7a5c43b..510da8fbb 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.th_th/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.th_th/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,3 +294,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.tr_tr/strings.po b/script.cu.lrclyrics/resources/language/resource.language.tr_tr/strings.po index f5fc5a947..216184f8f 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.tr_tr/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.tr_tr/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/language/resource.language.uk_ua/strings.po b/script.cu.lrclyrics/resources/language/resource.language.uk_ua/strings.po index 3fcedd9d8..96b9f6dd8 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.uk_ua/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.uk_ua/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.uz_uz/strings.po b/script.cu.lrclyrics/resources/language/resource.language.uz_uz/strings.po index 4aff5d1f6..398d6d793 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.uz_uz/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.uz_uz/strings.po @@ -147,6 +147,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -237,9 +245,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -247,7 +263,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -277,3 +293,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.vi_vn/strings.po b/script.cu.lrclyrics/resources/language/resource.language.vi_vn/strings.po index 7d452e618..990b7b5ac 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.vi_vn/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.vi_vn/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -278,3 +294,7 @@ msgstr "" msgctxt "#32916" msgid "Dark Lyrics" msgstr "" + +msgctxt "#32917" +msgid "Super Music" +msgstr "" diff --git a/script.cu.lrclyrics/resources/language/resource.language.zh_cn/strings.po b/script.cu.lrclyrics/resources/language/resource.language.zh_cn/strings.po index bd44c6137..02771416f 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.zh_cn/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.zh_cn/strings.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: XBMC Addons\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-07-27 10:14+0000\n" -"Last-Translator: taxigps \n" +"PO-Revision-Date: 2024-04-14 12:13+0000\n" +"Last-Translator: Christian Gade \n" "Language-Team: Chinese (China) \n" "Language: zh_cn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.13\n" +"X-Generator: Weblate 5.4.3\n" msgctxt "Addon Summary" msgid "CU LRC Lyrics" @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "搜索并保存 txt 歌词到歌曲目录" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,18 +246,26 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "如果隐藏音乐 OSD 和正在播放信息,歌词对话框的背景也将被隐藏。" +msgctxt "#32169" +msgid "Sources" +msgstr "来源" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" -msgstr "MiniLyrics" +msgid "Lrclib" +msgstr "" msgctxt "#32902" msgid "MusixMatch" msgstr "MusixMatch" msgctxt "#32903" -msgid "GomAudio" -msgstr "GomAudio" +msgid "Megalobiz" +msgstr "" msgctxt "#32904" msgid "Music 163" @@ -279,6 +295,19 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "Dark Lyrics" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + +# Scrapers - do not translate! +#~ msgctxt "#32901" +#~ msgid "MiniLyrics" +#~ msgstr "MiniLyrics" + +#~ msgctxt "#32903" +#~ msgid "GomAudio" +#~ msgstr "GomAudio" + #~ msgctxt "#32905" #~ msgid "Syair" #~ msgstr "Syair" diff --git a/script.cu.lrclyrics/resources/language/resource.language.zh_tw/strings.po b/script.cu.lrclyrics/resources/language/resource.language.zh_tw/strings.po index ba9604a40..cc169b320 100644 --- a/script.cu.lrclyrics/resources/language/resource.language.zh_tw/strings.po +++ b/script.cu.lrclyrics/resources/language/resource.language.zh_tw/strings.po @@ -148,6 +148,14 @@ msgctxt "#32119" msgid "Search for and save txt lyrics in song folder" msgstr "" +msgctxt "#32120" +msgid "%N. %A - %T" +msgstr "" + +msgctxt "#32121" +msgid "%A - %B - %N - %T" +msgstr "" + # empty strings from id 32120 to 32146 msgctxt "#32147" msgid "Search for local .txt lyrics files" @@ -238,9 +246,17 @@ msgctxt "#32168" msgid "If you hide the Music OSD and the Now Playing info, the background of the lyrics dialog will be hidden as well." msgstr "" +msgctxt "#32169" +msgid "Sources" +msgstr "" + +msgctxt "#32170" +msgid "Get lyrics when listening to internet radio stations" +msgstr "" + # Scrapers - do not translate! msgctxt "#32901" -msgid "MiniLyrics" +msgid "Lrclib" msgstr "" msgctxt "#32902" @@ -248,7 +264,7 @@ msgid "MusixMatch" msgstr "" msgctxt "#32903" -msgid "GomAudio" +msgid "Megalobiz" msgstr "" msgctxt "#32904" @@ -279,6 +295,10 @@ msgctxt "#32916" msgid "Dark Lyrics" msgstr "" +msgctxt "#32917" +msgid "Super Music" +msgstr "" + # empty strings from id 32010 to 32100 # Settings labels #~ msgctxt "#32101" diff --git a/script.cu.lrclyrics/resources/settings.xml b/script.cu.lrclyrics/resources/settings.xml index 9adb30629..5e3564389 100644 --- a/script.cu.lrclyrics/resources/settings.xml +++ b/script.cu.lrclyrics/resources/settings.xml @@ -22,12 +22,12 @@
- + 0 true - + 0 true @@ -42,6 +42,16 @@ true + + 0 + true + + + + 0 + true + + @@ -69,6 +79,11 @@ true + + 0 + true + + 0 true @@ -116,6 +131,8 @@ + + @@ -212,6 +229,13 @@ + + + 0 + false + + + diff --git a/script.domoticz.scenes/addon.xml b/script.domoticz.scenes/addon.xml index 3ef76abdc..45d37484e 100644 --- a/script.domoticz.scenes/addon.xml +++ b/script.domoticz.scenes/addon.xml @@ -1,5 +1,5 @@ - + @@ -18,15 +18,29 @@ https://github.com/Paul-VW/script.domoticz.scenes paulvw@live.com https://github.com/Paul-VW/script.domoticz.scenes - v0.0.1 (22/03/2021) -[new] beta Release + + v0.0.22 (02/11/2023) + [fix] End of list selected when cancel resolved + + v0.0.21 (31/10/2023) + [fix] Authentication issues -v0.0.8 (24/03/2021) -[new] Support for scenes, switches and favorites + v0.0.20 (26/10/2023) + [new] Domoticz 2023.2 support + [new] Ignore SSL Certificate error + [fix] HTTPS connection to domoticz + [fix] Toggle groups and start scenes + [fix] End of list implementation resolved -v0.0.10 (01/04/2021) -[fix] code cleanup -[fix] Domoticz authentication + v0.0.10 (01/04/2021) + [fix] code cleanup + [fix] Domoticz authentication + + v0.0.8 (24/03/2021) + [new] Support for scenes, switches and favorites + + v0.0.1 (22/03/2021) + [new] beta Release resources/icon.png diff --git a/script.domoticz.scenes/resources/language/resource.language.en_gb/strings.po b/script.domoticz.scenes/resources/language/resource.language.en_gb/strings.po index 9d343ef57..9fd35b2cc 100644 --- a/script.domoticz.scenes/resources/language/resource.language.en_gb/strings.po +++ b/script.domoticz.scenes/resources/language/resource.language.en_gb/strings.po @@ -52,10 +52,10 @@ msgctxt "#30407" msgid "Favorites" msgstr "Favorites" +msgctxt "#30408" +msgid "Ignore Certificate" +msgstr "Negeer certificaat" + msgctxt "#30498" msgid "Domoticz in Kodi" msgstr "Domoticz in Kodi" - -msgctxt "#30499" -msgid "------End of list------" -msgstr "------End of list------" diff --git a/script.domoticz.scenes/resources/language/resource.language.en_us/strings.po b/script.domoticz.scenes/resources/language/resource.language.en_us/strings.po index 9d343ef57..9fd35b2cc 100644 --- a/script.domoticz.scenes/resources/language/resource.language.en_us/strings.po +++ b/script.domoticz.scenes/resources/language/resource.language.en_us/strings.po @@ -52,10 +52,10 @@ msgctxt "#30407" msgid "Favorites" msgstr "Favorites" +msgctxt "#30408" +msgid "Ignore Certificate" +msgstr "Negeer certificaat" + msgctxt "#30498" msgid "Domoticz in Kodi" msgstr "Domoticz in Kodi" - -msgctxt "#30499" -msgid "------End of list------" -msgstr "------End of list------" diff --git a/script.domoticz.scenes/resources/language/resource.language.nl_nl/strings.po b/script.domoticz.scenes/resources/language/resource.language.nl_nl/strings.po index 37d6d8d35..99c48fc72 100644 --- a/script.domoticz.scenes/resources/language/resource.language.nl_nl/strings.po +++ b/script.domoticz.scenes/resources/language/resource.language.nl_nl/strings.po @@ -48,11 +48,10 @@ msgctxt "#30407" msgid "Favorites" msgstr "Favorieten" +msgctxt "#30408" +msgid "Ignore Certificate" +msgstr "Negeer certificaat" + msgctxt "#30498" msgid "Domoticz in Kodi" msgstr "Domoticz in Kodi" - -msgctxt "#30499" -msgid "------End of list------" -msgstr "------Einde------" - diff --git a/script.domoticz.scenes/resources/lib/script.py b/script.domoticz.scenes/resources/lib/script.py index 97a21e821..8b085325d 100644 --- a/script.domoticz.scenes/resources/lib/script.py +++ b/script.domoticz.scenes/resources/lib/script.py @@ -4,15 +4,14 @@ import xbmcaddon import sys import requests +from requests.auth import HTTPBasicAuth import json addon_handle = int(sys.argv[1]) xbmcplugin.setContent(addon_handle, 'videos') addonID = 'script.domoticz.scenes' -addonVersion = '0.0.10' -addonDate = "4/1/2021" - - +addonVersion = '0.0.21' +addonDate = "26/10/2023" __addon__ = xbmcaddon.Addon() __addonname__ = __addon__.getAddonInfo('name') @@ -30,124 +29,137 @@ domoticz_user = xbmcaddon.Addon(id=addonID).getSetting('domoticz_user') domoticz_pass = xbmcaddon.Addon(id=addonID).getSetting('domoticz_pass') domoticz_port = xbmcaddon.Addon(id=addonID).getSetting('domoticz_port') +domoticz_ssl = xbmcaddon.Addon(id=addonID).getSetting('SSL') +domoticz_ignore_ssl = xbmcaddon.Addon(id=addonID).getSetting('ignore_SSL') domoticz_group = xbmcaddon.Addon(id=addonID).getSetting('group') -def get_base_url(host, port, useSsl, username=None, password=None): - if useSsl is True: - base_url = "https://" + host + ":" + str(port) - else: - base_url = "http://" + host + ":" + str(port) +def domoticz_submit(query): + server = domoticz_host + port = domoticz_port + usr = domoticz_user + pwd = domoticz_pass + ssl = domoticz_ssl + server = server.replace("http://", "") + server = server.replace("https://", "") - if username is not None: - setcreds = 'username=' + username + '&password=' + password + '&' + if ssl == "true": + protocol = "https://" else: - setcreds = '' + protocol = "http://" - url = base_url + "/json.htm?" + setcreds - return url + url = protocol + server + ":" + str(port) + "/json.htm?" + query + + if domoticz_ignore_ssl == "true": + requests.packages.urllib3.disable_warnings() + if not usr and not pwd: + r = requests.get(url=url, verify=False) + else: + r = requests.get(url=url, auth=HTTPBasicAuth(usr, pwd), verify=False) + else: + if not usr and not pwd: + r = requests.get(url=url, verify=True) + else: + r = requests.get(url=url, auth=HTTPBasicAuth(usr, pwd), verify=False) + if r.status_code == 200: + result = r.text + data = json.loads(result) + return data -def get_scenes(base_url): - url = base_url + 'type=scenes' - try: - result = requests.get(url) - except: - return -1 - answer = result.content - jsonResult = json.loads(answer) - deviceResult = jsonResult['result'] - return(deviceResult) +def domoticz_get_version(): + query = "type=command¶m=getversion" + r = domoticz_submit(query=query) + return r -def get_favorite_devices_dict(base_url): - url = base_url + 'type=devices&used=true&filter=all&favorite=1' - try: - result = requests.get(url) - except: - return -1 +def domoticz_scenes_and_groups(): + domoticz_version = domoticz_get_version() + if domoticz_version['Revision'] > 15453 or domoticz_version['version'] == "2023.2 (build 15457)": + query = "type=command¶m=getscenes" + else: + query = 'type=scenes' - answer = result.content + r = domoticz_submit(query=query) - jsonResult = json.loads(answer) - deviceResult = jsonResult['result'] - return(deviceResult) + devices = r['result'] + scenes_and_groups = [] + groups_list = [] + scenes_list = [] + for dev in devices: + scenes_and_groups.append(dev) + if dev['Type'] == "Group": + groups_list.append(dev) + elif dev['Type'] == "Scene": + scenes_list.append(dev) + data = {"Groups": {"result": groups_list}, "Scenes": {"result": scenes_list}, "Groups and Scenes": {"result": scenes_and_groups}} + return data -def get_all_switches(base_url): - url = base_url + 'type=devices&filter=light&used=true&order=Name' - try: - result = requests.get(url) - except: - return -1 - answer = result.content +def domoticz_favorites(): + domoticz_version = domoticz_get_version() + if domoticz_version['Revision'] > 15453 or domoticz_version['version'] == "2023.2 (build 15457)": + query = "type=command¶m=getdevices&used=true&filter=all&favorite=1" + else: + query = 'type=devices&used=true&filter=all&favorite=1' + r = domoticz_submit(query=query) + return r - jsonResult = json.loads(answer) - deviceResult = jsonResult['result'] - return(deviceResult) +def domoticz_light_switches(): + domoticz_version = domoticz_get_version() + if domoticz_version['Revision'] > 15453 or domoticz_version['version'] == "2023.2 (build 15457)": + query = "type=command¶m=getdevices&filter=light&used=true&order=Name" + else: + query = 'type=devices&filter=light&used=true&order=Name' + r = domoticz_submit(query=query) + return r -def switch_scene(base_url, idx): - url = base_url + "type=command¶m=switchscene&idx=" + str(idx) + "&switchcmd=On" - requests.get(url=url) +def domoticz_start_scene(idx): + query = "type=command¶m=switchscene&idx=" + str(idx) + "&switchcmd=On" + r = domoticz_submit(query=query) + return r -def switch_switch(base_url, idx): - url = base_url + "type=command¶m=switchlight&idx=" + str(idx) + "&switchcmd=Toggle" - requests.get(url=url) +def domoticz_toggle_group(idx): + query = "type=command¶m=switchscene&idx=" + str(idx) + "&switchcmd=Toggle" + r = domoticz_submit(query=query) + return r -def switch_dimmer(base_url, idx, state): - url = base_url + "type=command¶m=switchlight&idx=" + str(idx) + "&switchcmd=Set%20Level&level=" + state - requests.get(url=url) +def domoticz_toggle_switch(idx): + query = "type=command¶m=switchlight&idx=" + str(idx) + "&switchcmd=Toggle" + r = domoticz_submit(query=query) + return r -def get_list(optionsDict): - end_list = __addon__.getLocalizedString(30499) +def create_optionsList(optionsDict): optionsList = [] for line in optionsDict: optionsList.append(line['Name']) - optionsList.append(end_list) return optionsList -base_url = get_base_url(host=domoticz_host, port=domoticz_port, useSsl=False, username=domoticz_user, password=domoticz_pass) - -if str(domoticz_group) == '0': - optionsDict = get_scenes(base_url=base_url) - optionsList = get_list(optionsDict=optionsDict) - -elif str(domoticz_group) == '1': - optionsDict = get_all_switches(base_url=base_url) - optionsList = get_list(optionsDict=optionsDict) - -elif str(domoticz_group) == '2': - optionsDict = get_favorite_devices_dict(base_url=base_url) - optionsList = get_list(optionsDict=optionsDict) - -else: - optionsDict = get_all_switches(base_url=base_url) - optionsList = get_list(optionsDict=optionsDict) - -title = __addon__.getLocalizedString(30498) - -answer = xbmcgui.Dialog().select(heading=title, list=optionsList) -action = optionsList[answer] - - def get_idx(optionsDict, action): - for line in optionsDict: + for line in optionsDict['result']: Name = line['Name'] if Name == action: idx = line['idx'] return idx +def get_group_scene_idx_type(optionsDict, action): + for dev in optionsDict['result']: + if dev['Name'] == action: + if "Type" in dev: + return dev['idx'], dev['Type'] + + def get_favorites_idx(optionsDict, action): - for line in optionsDict: + for line in optionsDict['result']: Name = line['Name'] if Name == action: idx = line['idx'] @@ -155,24 +167,64 @@ def get_favorites_idx(optionsDict, action): return idx, type -def run(): - end_list = __addon__.getLocalizedString(30499) +def create_optionsDict(): + if str(domoticz_group) == '0': + data = domoticz_scenes_and_groups() + optionsDict = data["Groups and Scenes"] + optionsList = create_optionsList(optionsDict=optionsDict['result']) + + elif str(domoticz_group) == '1': + optionsDict = domoticz_light_switches() + optionsList = create_optionsList(optionsDict=optionsDict['result']) + + elif str(domoticz_group) == '2': + optionsDict = domoticz_favorites() + optionsList = create_optionsList(optionsDict=optionsDict['result']) + + else: + optionsDict = domoticz_light_switches() + optionsList = create_optionsList(optionsDict=optionsDict['result']) + + return optionsDict, optionsList + + +title = __addon__.getLocalizedString(30498) - if action != end_list: +domoticz_version = domoticz_get_version() +optionsDict, optionsList = create_optionsDict() + +answer = xbmcgui.Dialog().select(heading=title, list=optionsList) +if answer == -1: + action = None +else: + action = optionsList[answer] + + +def run(): + if action and action in optionsList: if str(domoticz_group) == '0': - idx = get_idx(optionsDict=optionsDict, action=action) - switch_scene(base_url=base_url, idx=idx) + idx, groupType = get_group_scene_idx_type(optionsDict=optionsDict, action=action) + if groupType == "Scene": + domoticz_start_scene(idx=idx) + if groupType == "Group": + domoticz_toggle_group(idx=idx) elif str(domoticz_group) == '1': idx = get_idx(optionsDict=optionsDict, action=action) - switch_switch(base_url=base_url, idx=idx) + domoticz_toggle_switch(idx=idx) elif str(domoticz_group) == '2': idx, type = get_favorites_idx(optionsDict=optionsDict, action=action) if type == 'Scene': - switch_scene(base_url=base_url, idx=idx) + domoticz_start_scene(idx=idx) + if type == 'Group': + domoticz_toggle_group(idx=idx) if type == 'Light/Switch': - switch_switch(base_url=base_url, idx=idx) + domoticz_toggle_switch(idx=idx) if type == 'Color Switch': - switch_switch(base_url=base_url, idx=idx) + domoticz_toggle_switch(idx=idx) else: idx = get_idx(optionsDict=optionsDict, action=action) - switch_switch(base_url=base_url, idx=idx) + domoticz_toggle_switch(idx=idx) + + + + diff --git a/script.domoticz.scenes/resources/settings.xml b/script.domoticz.scenes/resources/settings.xml index c0efb762b..7d0f7d12f 100644 --- a/script.domoticz.scenes/resources/settings.xml +++ b/script.domoticz.scenes/resources/settings.xml @@ -2,8 +2,9 @@ - + + diff --git a/script.extendedinfo/addon.xml b/script.extendedinfo/addon.xml index f1e7d2d52..f7314c232 100644 --- a/script.extendedinfo/addon.xml +++ b/script.extendedinfo/addon.xml @@ -1,10 +1,13 @@ - + - + + + + executable @@ -21,7 +24,7 @@ http://forum.kodi.tv/showthread.php?tid=160558 https://github.com/scott967/script.extendedinfo scott967@kodi.tv - Python 3 fixes and Nexus/Matrix updates by scott967. Original addon for Leia and prior by phil65 (Philipp Temminghoff). Requires Kutils module version 1.3.0 from Kodi official repo + Python 3 fixes and Omega/Nexus/Matrix updates by scott967. Original addon for Leia and prior by phil65 (Philipp Temminghoff). resources/icon.png resources/fanart.jpg diff --git a/script.extendedinfo/changelog.txt b/script.extendedinfo/changelog.txt index a9298035d..09184aa84 100644 --- a/script.extendedinfo/changelog.txt +++ b/script.extendedinfo/changelog.txt @@ -1,3 +1,44 @@ +v6.0.7 +- reworked tmdb logon / accreditation +- various code refactoring / pylint items / docstrings (WIP) + +v6.0.5 +- removed support for broken/obsolete addons artwork downloader, couch potato, sick rage, trakt helper +- removed support for youtube-dl +- renamed all files to lowercase +- various code refactoring to current python 3.8 standards +- remove dependency on kutils (incorporate mdoules within addon) +- addon.xml bump ver 6.0.5 +- add menucontrol to skin files +- add dependency on autocompletion (required due to changes in autocompletion library) +- for Kodi 20, use new listitem setter classes/methods +- add "known for" for actor info when tmdb actor search as multiple results +- skin -- rearrange some actor info labels +- default.py switch to f-strings + add/update docstrings + wrap try/finally to clear state property on exception +- plugin.py fix exception V19 ListItem args deprecated +- process.py add/update docstrings + make import paths absolute + wrap try/finally to clear state property on exception +- theaudiodb.py add/update docstrings + remove dead TADB api key (patreon subscription now required) +- themoviedb.py add/update docstrings + add/update PEP 484 type annotations + debug logging message changes + switch to f-strings + fix exception due to key missing in dict (use .get method) +- windowmanager.py make import paths absolute + add/update docstrings + add a "hidebusy" function call to clear stuck busy dialog +- dialogactorinfo.py add/update docstrings + add/update PEP 484 type annotations +- dialogbaseinfo.py add/update docstrings + +- trakt.py add docstrings + fix get_episodes getting more than 20 episodes +- various bug fix/better handling of query results when error returned + v6.0.1 - Move codebase to python3 - Kodi Matrix / Nexus changes diff --git a/script.extendedinfo/default.py b/script.extendedinfo/default.py index 6a74f8664..7a19408ed 100644 --- a/script.extendedinfo/default.py +++ b/script.extendedinfo/default.py @@ -1,31 +1,35 @@ -# -*- coding: utf-8 -*- - # Copyright (C) 2015 - Philipp Temminghoff # Modifications copyright (C) 2022 - Scott Smart # This program is Free Software see LICENSE file for details +"""Module called by Runscript() as executable in Kodi +""" + +from __future__ import annotations import sys -from typing import Dict, List, Optional -from kutils import addon, utils +from resources.kutil131 import addon +from resources.kutil131 import utils from resources.lib import process -def pass_list_to_skin(name, data, prefix="", limit: Optional[int] = False) -> None: +def pass_list_to_skin(name: str, data, prefix: str = "", limit: int = False) -> None: """Set home window properties from the data - SetProperty(prefixname.%d.data_item key,data_item value, home) for 1 to - limit number of items in data + SetProperty(prefixname.%d.data_item key,data_item value, home) for 1 to + limit number of items in data Args: - name (str): Type of data being returned. Used to construct - the skin window property key eg. topratedmovies from invocation - parameter info= - data (kutils.itemlist.ItemList): collection of ListItems + name (str): Type of data being returned derived from runscript info + parameter. Used to construct the skin window property key + eg. topratedmovies from invocation parameter info= + data (kutils131.itemlist.ItemList): collection of ListItems (Video or Audio) - prefix (str, optional): Optional prefix for the name. Defaults to "". - limit (int, optional): Number of items to return. Defaults to False. + prefix (str, optional): Optional prefix for the name. May be set + as a param in runscript. Defaults to "". + limit (int, optional): Number of items to return. May be set as a param + in runscript. Defaults to False. Returns: None @@ -33,23 +37,21 @@ def pass_list_to_skin(name, data, prefix="", limit: Optional[int] = False) -> No if data and limit and int(limit) < len(data): data = data[:int(limit)] if not data: - addon.set_global('%s%s.Count' % (prefix, name), '0') + addon.set_global(f'{prefix}{name}.Count', '0') return None for (count, result) in enumerate(data): for (key, value) in result.get_infos().items(): - addon.set_global('%s%s.%i.%s' % - (prefix, name, count + 1, key), str(value)) + addon.set_global(f'{prefix}{name}.{count + 1}.{key}', str(value)) for key, value in result.get("properties", {}).items(): if not value: continue - addon.set_global('%s%s.%i.%s' % - (prefix, name, count + 1, key), str(value)) - addon.set_global('%s%s.Count' % (prefix, name), str(len(data))) + addon.set_global(f'{prefix}{name}.{count + 1}.{key}', str(value)) + addon.set_global(f'{prefix}{name}.Count', str(len(data))) class Main: """When called by Runscript provides all functionality. Multiple instances - of Main can be created. No class attributes or methods are provided + of Main can be created. No class attributes or methods are provided """ def __init__(self): @@ -60,7 +62,7 @@ def __init__(self): If started with no parameters, opens the video list dialog with movies from TMDB retrieved by popularity. """ - utils.log("version {} started".format(addon.VERSION)) + utils.log(f"version {addon.VERSION} started") addon.set_global("extendedinfo_running", "true") self._parse_argv() for info in self.infos: @@ -71,24 +73,27 @@ def __init__(self): limit=self.params.get("limit", 20)) if not self.infos: addon.set_global('infodialogs.active', "true") - from resources.lib.WindowManager import wm - wm.open_video_list() - addon.clear_global('infodialogs.active') + try: + from resources.lib.windowmanager import wm + wm.open_video_list() + finally: + addon.clear_global('infodialogs.active') addon.clear_global("extendedinfo_running") def _parse_argv(self) -> None: """gets arguments passed by Runscript call and passes them as - instance attributes of self.infos (invoked with info=) and self.params + instance attributes of self.infos list (invoked with info=) + and self.params dict """ - self.infos: List[str] = [] - self.params: Dict[str, str] = {"handle": None} + self.infos: list[str] = [] + self.params: dict[str, str] = {"handle": None} for arg in sys.argv[1:]: param = arg.replace('"', '').replace("'", " ") if param.startswith('info='): self.infos.append(param[5:]) else: try: - self.params[param.split("=")[0].lower()] = "=".join( + self.params[param.split('=', maxsplit=1)[0].lower()] = "=".join( param.split("=")[1:]).strip() except Exception: pass diff --git a/script.extendedinfo/plugin.py b/script.extendedinfo/plugin.py index 99514e036..def23c071 100644 --- a/script.extendedinfo/plugin.py +++ b/script.extendedinfo/plugin.py @@ -1,18 +1,19 @@ -# -*- coding: utf8 -*- - # Copyright (C) 2015 - Philipp Temminghoff # Modifications copyright (C) 2022 - Scott Smart # This program is Free Software see LICENSE file for details +"""Entry point when called as video plugin +""" + import os import sys import routing import xbmcgui import xbmcplugin +from resources.kutil131 import addon -from kutils import addon, utils - +from resources.kutil131 import utils from resources.lib import process MOVIEDB_IMAGE = os.path.join(addon.MEDIA_PATH, "moviedb.png") @@ -22,9 +23,14 @@ class Main: + """Handles plugin list / listitem creation + """ def __init__(self): - utils.log("version %s started" % addon.VERSION) + """Constructor gets actions from args to create the + plugin list + """ + utils.log(f"plugin version {addon.VERSION} started") addon.set_global("extendedinfo_running", "true") self._parse_argv() for info in self.infos: @@ -43,9 +49,13 @@ def _parse_argv(self): if args.startswith("---"): delimiter = "&" args = args[3:] + elif args.find("&---"): + delimiter = "&" else: delimiter = "&&" for arg in args.split(delimiter): + if arg.startswith("---"): + arg = arg[3:] param = arg.replace('"', '').replace("'", " ") if param.startswith('info='): self.infos.append(param[5:]) @@ -59,6 +69,8 @@ def _parse_argv(self): @plugin.route('/tmdb') def tmdb(): + """_sets category options for tmdb + """ xbmcplugin.setPluginCategory(plugin.handle, "TheMovieDB") items = [("incinemamovies", addon.LANG(32042)), ("upcomingmovies", addon.LANG(32043)), @@ -77,9 +89,9 @@ def tmdb(): if addon.setting("tmdb_username") and addon.setting("tmdb_password"): items += login for key, value in items: - li = xbmcgui.ListItem(label=value, - thumbnailImage="DefaultFolder.png") - url = 'plugin://script.extendedinfo?info=%s' % key + li = xbmcgui.ListItem(label=value) + li.setArt({'thumb': 'DefaultFolder.png'}) + url = f'plugin://script.extendedinfo?info={key}' xbmcplugin.addDirectoryItem(handle=plugin.handle, url=url, listitem=li, @@ -90,6 +102,8 @@ def tmdb(): @plugin.route('/trakt') def trakt(): + """sets category options form trakt + """ xbmcplugin.setPluginCategory(plugin.handle, "Trakt") items = [("trendingmovies", addon.LANG(32047)), ("traktpopularmovies", addon.LANG(32044)), @@ -107,9 +121,9 @@ def trakt(): ("airingepisodes", addon.LANG(32028)), ("premiereepisodes", addon.LANG(32029))] for key, value in items: - li = xbmcgui.ListItem(label=value, - thumbnailImage="DefaultFolder.png") - url = 'plugin://script.extendedinfo?info=%s' % key + li = xbmcgui.ListItem(label=value) + li.setArt({'thumb': 'DefaultFolder.png'}) + url = f'plugin://script.extendedinfo?info={key}' xbmcplugin.addDirectoryItem(handle=plugin.handle, url=url, listitem=li, @@ -120,11 +134,15 @@ def trakt(): @plugin.route('/') def root(): + """Sets root plugin folder for TMDB and Trakt + """ + traktitem = xbmcgui.ListItem(label="Trakt") + traktitem.setArt({'thumb': TRAKT_IMAGE}) + tmdbitem = xbmcgui.ListItem(label="TheMovieDB") + tmdbitem.setArt({'thumb': MOVIEDB_IMAGE}) items = [ - (plugin.url_for(trakt), xbmcgui.ListItem(label="Trakt", - thumbnailImage=TRAKT_IMAGE), True), - (plugin.url_for(tmdb), xbmcgui.ListItem(label="TheMovieDB", - thumbnailImage=MOVIEDB_IMAGE), True), + (plugin.url_for(trakt), traktitem, True), + (plugin.url_for(tmdb), tmdbitem, True), ] xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addDirectoryItems(plugin.handle, items) diff --git a/script.extendedinfo/resources/kutil131/__init__.py b/script.extendedinfo/resources/kutil131/__init__.py new file mode 100644 index 000000000..e6c907b3e --- /dev/null +++ b/script.extendedinfo/resources/kutil131/__init__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +from resources.kutil131.kodiaddon import Addon + +addon = Addon() + +import sys + +from resources.kutil131.abs_last_fm import AbstractLastFM +from resources.kutil131.actionhandler import ActionHandler +from resources.kutil131.busyhandler import busyhandler as busy +from resources.kutil131.dialogbaselist import DialogBaseList +from resources.kutil131.itemlist import ItemList +from resources.kutil131.kodilogging import KodiLogHandler, config +from resources.kutil131.listitem import AudioItem, ListItem, VideoItem +from resources.kutil131.localdb import LocalDB +from resources.kutil131.player import VideoPlayer + +# PIL uses numpy if it is available. +# numpy can NOT be loaded multiple times in a sub-interpreter. +# Since numpy is optional, force it to NEVER be imported. + +sys.modules['numpy'] = None + +local_db: LocalDB = LocalDB(last_fm=AbstractLastFM()) +player = VideoPlayer() diff --git a/script.extendedinfo/resources/kutil131/abs_last_fm.py b/script.extendedinfo/resources/kutil131/abs_last_fm.py new file mode 100644 index 000000000..c210db726 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/abs_last_fm.py @@ -0,0 +1,12 @@ +""" +Created on 8/24/21 + +@author: Frank Feuerbacher +""" + + +class AbstractLastFM: + + @classmethod + def get_similar_artists(cls, artist_mbid): + pass \ No newline at end of file diff --git a/script.extendedinfo/resources/kutil131/actionhandler.py b/script.extendedinfo/resources/kutil131/actionhandler.py new file mode 100644 index 000000000..bacc5d441 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/actionhandler.py @@ -0,0 +1,331 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmcgui + +ACTIONS = {"none": xbmcgui.ACTION_NONE, + "left": xbmcgui.ACTION_MOVE_LEFT, + "right": xbmcgui.ACTION_MOVE_RIGHT, + "up": xbmcgui.ACTION_MOVE_UP, + "down": xbmcgui.ACTION_MOVE_DOWN, + "pageup": xbmcgui.ACTION_PAGE_UP, + "pagedown": xbmcgui.ACTION_PAGE_DOWN, + "select": xbmcgui.ACTION_SELECT_ITEM, + "highlight": xbmcgui.ACTION_HIGHLIGHT_ITEM, + "parentdir": xbmcgui.ACTION_NAV_BACK, # backward compatibility + "parentfolder": xbmcgui.ACTION_PARENT_DIR, + "back": xbmcgui.ACTION_NAV_BACK, + "previousmenu": xbmcgui.ACTION_PREVIOUS_MENU, + "info": xbmcgui.ACTION_SHOW_INFO, + "pause": xbmcgui.ACTION_PAUSE, + "stop": xbmcgui.ACTION_STOP, + "skipnext": xbmcgui.ACTION_NEXT_ITEM, + "skipprevious": xbmcgui.ACTION_PREV_ITEM, + "fullscreen": xbmcgui.ACTION_SHOW_GUI, + "aspectratio": xbmcgui.ACTION_ASPECT_RATIO, + "stepforward": xbmcgui.ACTION_STEP_FORWARD, + "stepback": xbmcgui.ACTION_STEP_BACK, + "bigstepforward": xbmcgui.ACTION_BIG_STEP_FORWARD, + "bigstepback": xbmcgui.ACTION_BIG_STEP_BACK, + "chapterorbigstepforward": xbmcgui.ACTION_CHAPTER_OR_BIG_STEP_FORWARD, + "chapterorbigstepback": xbmcgui.ACTION_CHAPTER_OR_BIG_STEP_BACK, + "osd": xbmcgui.ACTION_SHOW_OSD, + "showsubtitles": xbmcgui.ACTION_SHOW_SUBTITLES, + "nextsubtitle": xbmcgui.ACTION_NEXT_SUBTITLE, + "cyclesubtitle": xbmcgui.ACTION_CYCLE_SUBTITLE, + "playerdebug": xbmcgui.ACTION_PLAYER_DEBUG, + "nextpicture": xbmcgui.ACTION_NEXT_PICTURE, + "previouspicture": xbmcgui.ACTION_PREV_PICTURE, + "zoomout": xbmcgui.ACTION_ZOOM_OUT, + "zoomin": xbmcgui.ACTION_ZOOM_IN, + "playlist": xbmcgui.ACTION_SHOW_PLAYLIST, + "queue": xbmcgui.ACTION_QUEUE_ITEM, + "zoomnormal": xbmcgui.ACTION_ZOOM_LEVEL_NORMAL, + "zoomlevel1": xbmcgui.ACTION_ZOOM_LEVEL_1, + "zoomlevel2": xbmcgui.ACTION_ZOOM_LEVEL_2, + "zoomlevel3": xbmcgui.ACTION_ZOOM_LEVEL_3, + "zoomlevel4": xbmcgui.ACTION_ZOOM_LEVEL_4, + "zoomlevel5": xbmcgui.ACTION_ZOOM_LEVEL_5, + "zoomlevel6": xbmcgui.ACTION_ZOOM_LEVEL_6, + "zoomlevel7": xbmcgui.ACTION_ZOOM_LEVEL_7, + "zoomlevel8": xbmcgui.ACTION_ZOOM_LEVEL_8, + "zoomlevel9": xbmcgui.ACTION_ZOOM_LEVEL_9, + "nextcalibration": xbmcgui.ACTION_CALIBRATE_SWAP_ARROWS, + "resetcalibration": xbmcgui.ACTION_CALIBRATE_RESET, + "analogmove": xbmcgui.ACTION_ANALOG_MOVE, + "rotate": xbmcgui.ACTION_ROTATE_PICTURE_CW, + "rotateccw": xbmcgui.ACTION_ROTATE_PICTURE_CCW, + "close": xbmcgui.ACTION_NAV_BACK, # backwards compatibility + "subtitledelayminus": xbmcgui.ACTION_SUBTITLE_DELAY_MIN, + "subtitledelay": xbmcgui.ACTION_SUBTITLE_DELAY, + "subtitledelayplus": xbmcgui.ACTION_SUBTITLE_DELAY_PLUS, + "audiodelayminus": xbmcgui.ACTION_AUDIO_DELAY_MIN, + "audiodelay": xbmcgui.ACTION_AUDIO_DELAY, + "audiodelayplus": xbmcgui.ACTION_AUDIO_DELAY_PLUS, + "subtitleshiftup": xbmcgui.ACTION_SUBTITLE_VSHIFT_UP, + "subtitleshiftdown": xbmcgui.ACTION_SUBTITLE_VSHIFT_DOWN, + "subtitlealign": xbmcgui.ACTION_SUBTITLE_ALIGN, + "audionextlanguage": xbmcgui.ACTION_AUDIO_NEXT_LANGUAGE, + "verticalshiftup": xbmcgui.ACTION_VSHIFT_UP, + "verticalshiftdown": xbmcgui.ACTION_VSHIFT_DOWN, + "nextresolution": xbmcgui.ACTION_CHANGE_RESOLUTION, + "audiotoggledigital": xbmcgui.ACTION_TOGGLE_DIGITAL_ANALOG, + "number0": xbmcgui.REMOTE_0, + "number1": xbmcgui.REMOTE_1, + "number2": xbmcgui.REMOTE_2, + "number3": xbmcgui.REMOTE_3, + "number4": xbmcgui.REMOTE_4, + "number5": xbmcgui.REMOTE_5, + "number6": xbmcgui.REMOTE_6, + "number7": xbmcgui.REMOTE_7, + "number8": xbmcgui.REMOTE_8, + "number9": xbmcgui.REMOTE_9, + "smallstepback": xbmcgui.ACTION_SMALL_STEP_BACK, + "fastforward": xbmcgui.ACTION_PLAYER_FORWARD, + "rewind": xbmcgui.ACTION_PLAYER_REWIND, + "play": xbmcgui.ACTION_PLAYER_PLAY, + "playpause": xbmcgui.ACTION_PLAYER_PLAYPAUSE, + "switchplayer": xbmcgui.ACTION_SWITCH_PLAYER, + "delete": xbmcgui.ACTION_DELETE_ITEM, + "copy": xbmcgui.ACTION_COPY_ITEM, + "move": xbmcgui.ACTION_MOVE_ITEM, + "screenshot": xbmcgui.ACTION_TAKE_SCREENSHOT, + "rename": xbmcgui.ACTION_RENAME_ITEM, + "togglewatched": xbmcgui.ACTION_TOGGLE_WATCHED, + "scanitem": xbmcgui.ACTION_SCAN_ITEM, + "reloadkeymaps": xbmcgui.ACTION_RELOAD_KEYMAPS, + "volumeup": xbmcgui.ACTION_VOLUME_UP, + "volumedown": xbmcgui.ACTION_VOLUME_DOWN, + "mute": xbmcgui.ACTION_MUTE, + "backspace": xbmcgui.ACTION_BACKSPACE, + "scrollup": xbmcgui.ACTION_SCROLL_UP, + "scrolldown": xbmcgui.ACTION_SCROLL_DOWN, + "analogfastforward": xbmcgui.ACTION_ANALOG_FORWARD, + "analogrewind": xbmcgui.ACTION_ANALOG_REWIND, + "moveitemup": xbmcgui.ACTION_MOVE_ITEM_UP, + "moveitemdown": xbmcgui.ACTION_MOVE_ITEM_DOWN, + "contextmenu": xbmcgui.ACTION_CONTEXT_MENU, + "shift": xbmcgui.ACTION_SHIFT, + "symbols": xbmcgui.ACTION_SYMBOLS, + "cursorleft": xbmcgui.ACTION_CURSOR_LEFT, + "cursorright": xbmcgui.ACTION_CURSOR_RIGHT, + "built-in": xbmcgui.ACTION_BUILT_IN_FUNCTION, + "showtime": xbmcgui.ACTION_SHOW_OSD_TIME, + "analogseekforward": xbmcgui.ACTION_ANALOG_SEEK_FORWARD, + "analogseekback": xbmcgui.ACTION_ANALOG_SEEK_BACK, + "showpreset": xbmcgui.ACTION_VIS_PRESET_SHOW, + "nextpreset": xbmcgui.ACTION_VIS_PRESET_NEXT, + "previouspreset": xbmcgui.ACTION_VIS_PRESET_PREV, + "lockpreset": xbmcgui.ACTION_VIS_PRESET_LOCK, + "randompreset": xbmcgui.ACTION_VIS_PRESET_RANDOM, + "increasevisrating": xbmcgui.ACTION_VIS_RATE_PRESET_PLUS, + "decreasevisrating": xbmcgui.ACTION_VIS_RATE_PRESET_MINUS, + "showvideomenu": xbmcgui.ACTION_SHOW_VIDEOMENU, + "enter": xbmcgui.ACTION_ENTER, + "increaserating": xbmcgui.ACTION_INCREASE_RATING, + "decreaserating": xbmcgui.ACTION_DECREASE_RATING, + "togglefullscreen": xbmcgui.ACTION_TOGGLE_FULLSCREEN, + "nextscene": xbmcgui.ACTION_NEXT_SCENE, + "previousscene": xbmcgui.ACTION_PREV_SCENE, + "nextletter": xbmcgui.ACTION_NEXT_LETTER, + "prevletter": xbmcgui.ACTION_PREV_LETTER, + "jumpsms2": xbmcgui.ACTION_JUMP_SMS2, + "jumpsms3": xbmcgui.ACTION_JUMP_SMS3, + "jumpsms4": xbmcgui.ACTION_JUMP_SMS4, + "jumpsms5": xbmcgui.ACTION_JUMP_SMS5, + "jumpsms6": xbmcgui.ACTION_JUMP_SMS6, + "jumpsms7": xbmcgui.ACTION_JUMP_SMS7, + "jumpsms8": xbmcgui.ACTION_JUMP_SMS8, + "jumpsms9": xbmcgui.ACTION_JUMP_SMS9, + "filter": xbmcgui.ACTION_FILTER, + "filterclear": xbmcgui.ACTION_FILTER_CLEAR, + "filtersms2": xbmcgui.ACTION_FILTER_SMS2, + "filtersms3": xbmcgui.ACTION_FILTER_SMS3, + "filtersms4": xbmcgui.ACTION_FILTER_SMS4, + "filtersms5": xbmcgui.ACTION_FILTER_SMS5, + "filtersms6": xbmcgui.ACTION_FILTER_SMS6, + "filtersms7": xbmcgui.ACTION_FILTER_SMS7, + "filtersms8": xbmcgui.ACTION_FILTER_SMS8, + "filtersms9": xbmcgui.ACTION_FILTER_SMS9, + "firstpage": xbmcgui.ACTION_FIRST_PAGE, + "lastpage": xbmcgui.ACTION_LAST_PAGE, + "menu": xbmcgui.ACTION_MENU, + "guiprofile": xbmcgui.ACTION_GUIPROFILE_BEGIN, + "red": xbmcgui.ACTION_TELETEXT_RED, + "green": xbmcgui.ACTION_TELETEXT_GREEN, + "yellow": xbmcgui.ACTION_TELETEXT_YELLOW, + "blue": xbmcgui.ACTION_TELETEXT_BLUE, + "increasepar": xbmcgui.ACTION_INCREASE_PAR, + "decreasepar": xbmcgui.ACTION_DECREASE_PAR, + "volampup": xbmcgui.ACTION_VOLAMP_UP, + "volampdown": xbmcgui.ACTION_VOLAMP_DOWN, + "createbookmark": xbmcgui.ACTION_CREATE_BOOKMARK, + "createepisodebookmark": xbmcgui.ACTION_CREATE_EPISODE_BOOKMARK, + "settingsreset": xbmcgui.ACTION_SETTINGS_RESET, + "settingslevelchange": xbmcgui.ACTION_SETTINGS_LEVEL_CHANGE, # 3D movie playback/GUI + "stereomode": xbmcgui.ACTION_STEREOMODE_SELECT, # cycle 3D modes, for now an alias for next + "nextstereomode": xbmcgui.ACTION_STEREOMODE_NEXT, + "previousstereomode": xbmcgui.ACTION_STEREOMODE_PREVIOUS, + "togglestereomode": xbmcgui.ACTION_STEREOMODE_TOGGLE, + "stereomodetomono": xbmcgui.ACTION_STEREOMODE_TOMONO, # PVR actions + "channelup": xbmcgui.ACTION_CHANNEL_UP, + "channeldown": xbmcgui.ACTION_CHANNEL_DOWN, + "previouschannelgroup": xbmcgui.ACTION_PREVIOUS_CHANNELGROUP, + "nextchannelgroup": xbmcgui.ACTION_NEXT_CHANNELGROUP, + "playpvr": xbmcgui.ACTION_PVR_PLAY, + "playpvrtv": xbmcgui.ACTION_PVR_PLAY_TV, + "playpvrradio": xbmcgui.ACTION_PVR_PLAY_RADIO, + "record": xbmcgui.ACTION_RECORD, # Mouse actions + "leftclick": xbmcgui.ACTION_MOUSE_LEFT_CLICK, + "rightclick": xbmcgui.ACTION_MOUSE_RIGHT_CLICK, + "middleclick": xbmcgui.ACTION_MOUSE_MIDDLE_CLICK, + "doubleclick": xbmcgui.ACTION_MOUSE_DOUBLE_CLICK, + "longclick": xbmcgui.ACTION_MOUSE_LONG_CLICK, + "wheelup": xbmcgui.ACTION_MOUSE_WHEEL_UP, + "wheeldown": xbmcgui.ACTION_MOUSE_WHEEL_DOWN, + "mousedrag": xbmcgui.ACTION_MOUSE_DRAG, + "mousemove": xbmcgui.ACTION_MOUSE_MOVE, # Touch + "tap": xbmcgui.ACTION_TOUCH_TAP, + "longpress": xbmcgui.ACTION_TOUCH_LONGPRESS, + "pangesture": xbmcgui.ACTION_GESTURE_PAN, + "zoomgesture": xbmcgui.ACTION_GESTURE_ZOOM, + "rotategesture": xbmcgui.ACTION_GESTURE_ROTATE, + "swipeleft": xbmcgui.ACTION_GESTURE_SWIPE_LEFT, + "swiperight": xbmcgui.ACTION_GESTURE_SWIPE_RIGHT, + "swipeup": xbmcgui.ACTION_GESTURE_SWIPE_UP, + "swipedown": xbmcgui.ACTION_GESTURE_SWIPE_DOWN, + + # Do nothing / error action + "noop": xbmcgui.ACTION_NOOP + } + + +class ActionHandler(): + + def __init__(self): + self.clicks = {} + self.focus_actions = {} + self.action_maps = {} + self.context_actions = {} + self.info_actions = {} + self.clicks_by_type = {} + + def click(self, button_ids): + def decorator(f): + if isinstance(button_ids, list): + for button_id in button_ids: + self.clicks[button_id] = f + else: + self.clicks[button_ids] = f + return f + + return decorator + + def click_by_type(self, mediatypes): + def decorator(f): + if isinstance(mediatypes, list): + for mediatype in mediatypes: + self.clicks_by_type[mediatype] = f + else: + self.clicks_by_type[mediatypes] = f + return f + + return decorator + + def context(self, mediatypes): + def decorator(f): + if isinstance(mediatypes, list): + for mediatype in mediatypes: + self.context_actions[mediatype] = f + else: + self.context_actions[mediatypes] = f + return f + + return decorator + + def info(self, mediatypes): + def decorator(f): + if isinstance(mediatypes, list): + for mediatype in mediatypes: + self.info_actions[mediatype] = f + else: + self.info_actions[mediatypes] = f + return f + + return decorator + + def action(self, builtin_name, button_ids): + def decorator(f): + action_id = ACTIONS[builtin_name] + if action_id not in self.action_maps: + self.action_maps[action_id] = {} + if isinstance(button_ids, list): + for button_id in button_ids: + self.action_maps[action_id][button_id] = f + else: + self.action_maps[action_id][button_ids] = f + return f + + return decorator + + def focus(self, button_ids): + def decorator(f): + if isinstance(button_ids, list): + for button_id in button_ids: + self.focus_actions[button_id] = f + else: + self.focus_actions[button_ids] = f + return f + + return decorator + + def serve(self, control_id, wnd): + listitem = self.get_listitem(wnd, control_id) + if listitem: + media_type = listitem.getVideoInfoTag().getMediaType() + if media_type: + action = self.clicks_by_type.get(media_type) + if action: + action(wnd, control_id) + view_function = self.clicks.get(control_id) + if view_function: + return view_function(wnd, control_id) + + def serve_focus(self, control_id, wnd): + view_function = self.focus_actions.get(control_id) + if view_function: + return view_function(wnd, control_id) + + def serve_action(self, action: xbmcgui.Action, control_id, wnd): + action_id = action.getId() + wnd.action_id = action_id + if action_id in [ACTIONS["contextmenu"], ACTIONS["info"]]: + listitem = self.get_listitem(wnd, control_id) + if listitem: + media_type = listitem.getVideoInfoTag().getMediaType() + if media_type: + action = None + if action_id == ACTIONS["contextmenu"]: + action = self.context_actions.get(media_type) + else: + action = self.info_actions.get(media_type) + if action: + action(wnd, control_id) + if action_id not in self.action_maps: + return None + dct = self.action_maps[action_id] + all_func = dct.get("*") + if all_func: + all_func(wnd, control_id) + ctl_func = dct.get(control_id) + if ctl_func: + return ctl_func(wnd, control_id) + + def get_listitem(self, wnd, control_id): + try: + listitem = wnd.getControl(control_id).getSelectedItem() + if not listitem: + listitem = wnd.getListItem(wnd.getCurrentListPosition()) + return listitem + except Exception: + return None diff --git a/script.extendedinfo/resources/kutil131/busyhandler.py b/script.extendedinfo/resources/kutil131/busyhandler.py new file mode 100644 index 000000000..06440077c --- /dev/null +++ b/script.extendedinfo/resources/kutil131/busyhandler.py @@ -0,0 +1,77 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import traceback +from functools import wraps + +import xbmc +import xbmcgui + +from resources.kutil131 import utils + + +class BusyHandler: + """ + Class to deal with busydialog handling + """ + def __init__(self, *args, **kwargs): + self.busy = 0 + self.enabled = True + + def enable(self): + """ + Enables busydialog handling + """ + self.enabled = True + + def disable(self): + """ + Disables busydialog handling + """ + self.enabled = False + + def show_busy(self): + """ + Increase busycounter and open busydialog if needed + """ + if not self.enabled: + return None + if self.busy == 0: + xbmc.executebuiltin('ActivateWindow(busydialognocancel)') + self.busy += 1 + + def set_progress(self, percent): + pass + + def hide_busy(self): + """ + Decrease busycounter and close busydialog if needed + """ + if not self.enabled: + return None + self.busy = max(0, self.busy - 1) + if self.busy == 0: + # utils.log('Closing busydialognoancel') + xbmc.executebuiltin('Dialog.Close(busydialognocancel)') + + def set_busy(self, func): + """ + Decorator to show busy dialog while function is running + """ + @wraps(func) + def decorator(cls, *args, **kwargs): + self.show_busy() + result = None + try: + result = func(cls, *args, **kwargs) + except Exception: + utils.log(traceback.format_exc()) + utils.notify("Error", "please contact add-on author") + finally: + self.hide_busy() + return result + + return decorator + + +busyhandler = BusyHandler() diff --git a/script.extendedinfo/resources/kutil131/confirmdialog.py b/script.extendedinfo/resources/kutil131/confirmdialog.py new file mode 100644 index 000000000..bce52249e --- /dev/null +++ b/script.extendedinfo/resources/kutil131/confirmdialog.py @@ -0,0 +1,61 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmc +import xbmcgui + +from resources.kutil131 import addon + +ID_BUTTON_YES = 11 +ID_BUTTON_NO = 10 +ID_BUTTON_EXTRA = 12 +ID_LABEL_HEADER = 1 +ID_LABEL_TEXT = 9 +ID_PROGRESS = 20 + + +class ConfirmDialog(xbmcgui.WindowXMLDialog): + """ + open yesnodialog, return -1 for cancelled, otherwise index (0-2) + """ + def __init__(self, *args, **kwargs): + xbmcgui.WindowXMLDialog.__init__(self, 'ConfirmDialog.xml', kwargs.get('path', '')) + self.yeslabel = kwargs.get('yeslabel') + self.nolabel = kwargs.get('nolabel') + self.header = kwargs.get('header') + self.text = kwargs.get('text') + self.extrabutton = kwargs.get('extrabutton') + self.index = -1 + + def onInit(self): + self.setFocusId(10) + self.getControl(ID_BUTTON_YES).setLabel(self.yeslabel) + self.getControl(ID_BUTTON_NO).setLabel(self.nolabel) + self.getControl(ID_LABEL_HEADER).setLabel(self.header) + self.getControl(ID_LABEL_TEXT).setText(self.text) + if self.extrabutton: + self.getControl(ID_BUTTON_EXTRA).setVisible(True) + self.getControl(ID_BUTTON_EXTRA).setLabel(self.extrabutton) + else: + self.getControl(ID_BUTTON_EXTRA).setVisible(False) + self.getControl(ID_PROGRESS).setVisible(False) + self.setFocusId(ID_BUTTON_NO) + + def onClick(self, control_id): + self.index = control_id - ID_BUTTON_NO + self.close() + + +def open(header="", text="", yeslabel=addon.LANG(107), nolabel=addon.LANG(106), extrabutton=False, path=""): + """ + open yesnodialog, return -1 for cancelled, otherwise index (0-2) + """ + xbmc.executebuiltin("Dialog.Close(busydialognocancel)") + w = ConfirmDialog('DialogConfirm.xml', path, + yeslabel=yeslabel, + nolabel=nolabel, + header=header, + text=text, + extrabutton=extrabutton) + w.doModal() + return w.index diff --git a/script.extendedinfo/resources/kutil131/dialogbaselist.py b/script.extendedinfo/resources/kutil131/dialogbaselist.py new file mode 100644 index 000000000..c8187d515 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/dialogbaselist.py @@ -0,0 +1,435 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +from __future__ import annotations +import xbmc +import xbmcgui + +from resources.kutil131 import ActionHandler, addon, busy, utils +from resources.kutil131.t9_search import T9Search + +ch = ActionHandler() + +ID_BUTTON_SEARCH = 6000 +ID_BUTTON_RESETFILTERS = 5005 +ID_BUTTON_PREV_PAGE = 700 +ID_BUTTON_NEXT_PAGE = 600 +ID_BUTTON_TOGGLETYPE = 5007 + + +class DialogBaseList: + viewid = { + 'WALL 3D' : '67', + 'BANNER' : '52', + 'BANNER INFO' : '53', + 'COVERFLOW' : '58', + 'GAMES' : '50', + 'GLASS LIST' : '57', + 'LANDSCAPEX' : '64', + 'LOW LIST' : '55', + 'MID LIST' : '70', + 'MULTI-LIST' : '71', + 'MULTIPLEX' : '61', + 'PANEL' : '62', + 'PHOTOSHOW' : '79', + 'POSTER' : '54', + 'RIGHT LIST' : '59', + 'SETS' : '65', + 'SHELF' : '63', + 'SHELFCASE' : '69', + 'SHOWART' : '60', + 'SHOWCASE' : '66', + 'WALL' : '56', + 'WIDE' : '51', + 'List' : '50', + 'IconWall' : '52', + 'Shift' : '53', + 'InfoWall' : '54', + 'WideList' : '55', + 'Banner' : '501', + 'Fanart' : '502' + } + + + + """ + BaseList for MediaBrowsers (handles filtering, sorting) + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.search_str = kwargs.get('search_str', "") + self.filter_label = kwargs.get("filter_label", "") + self.mode = kwargs.get("mode", "filter") + self.order = kwargs.get('order', "desc") + self.sort = kwargs.get('sort', self.default_sort) + self.filters = [] + for item in kwargs.get('filters', []): + self.add_filter(key=item["type"], + value=item["id"], + label=item["label"], + reset=False) + self.page = 1 + self.listitems = None + self.column = None + self.set_sort(self.sort) + self.last_position = 0 + self.total_pages = 1 + self.total_items = 0 + self.page_token = "" + self.next_page_token = "" + self.prev_page_token = "" + self.listitems = kwargs.get('listitems') + if self.listitems: + self.listitems = self.listitems.create_listitems() + self.total_items = len(self.listitems) + else: + self.update_content(force_update=kwargs.get('force', False)) + + def onInit(self): + super().onInit() + viewtype = addon.setting("viewtype_selection") + self.cur_viewtype = DialogBaseList.viewid.get(xbmc.getInfoLabel('Container.Viewmode')) + if viewtype: + xbmc.executebuiltin("Container.SetViewMode(%s)" % viewtype) + self.update_ui() + if self.total_items > 0: + self.setFocusId(self.getCurrentContainerId()) + self.setCurrentListPosition(self.last_position) + else: + self.setFocusId(ID_BUTTON_SEARCH) + + def onClick(self, control_id): + ch.serve(control_id, self) + + def onAction(self, action: xbmcgui.Action): + ch.serve_action(action, self.getFocusId(), self) + + def onFocus(self, control_id): + if control_id == ID_BUTTON_NEXT_PAGE: + self.go_to_next_page() + elif control_id == ID_BUTTON_PREV_PAGE: + self.go_to_prev_page() + + @property + def sort_key(self): + """ + get key used for sorting + """ + return self.type + + def close(self): + """ + save viewtype and last focusposition + """ + addon.set_setting("viewtype_selection", str(self.getCurrentContainerId())) + self.last_position = self.getCurrentListPosition() + xbmc.executebuiltin("Container.SetViewMode(%s)" % self.cur_viewtype) + super().close() + + def set_sort(self, sort): + """ + set sort method to *sort + """ + self.sort = sort + self.verify_sort() + self.sort_label = self.SORTS[self.sort_key][self.sort] + + def verify_sort(self): + """ + check if sort is valid. If not, change to default. + """ + if self.sort not in [i for i in list(self.SORTS[self.sort_key].keys())]: + self.set_sort(self.default_sort) + + @ch.click(ID_BUTTON_TOGGLETYPE) + def toggle_type(self, control_id): + """ + toggle type + """ + self.filters = [] + self.type = self.TYPES[self.TYPES.index(self.type) - 1] + self.verify_sort() + self.reset() + + @ch.click(ID_BUTTON_RESETFILTERS) + def reset_filters(self, control_id): + """ + reset filters, show selectdialog if filtercount > 1 + """ + if len(self.filters) > 1: + listitems = ["%s: %s" % (f["typelabel"], f["label"]) for f in self.filters] + listitems.append(addon.LANG(32078)) + index = xbmcgui.Dialog().select(heading=addon.LANG(32077), + list=listitems) + if index == -1: + return None + elif index == len(listitems) - 1: + self.filters = [] + else: + del self.filters[index] + else: + self.filters = [] + self.reset() + + @ch.click(ID_BUTTON_SEARCH) + def open_search(self, control_id): + """ + open search dialog, update list with search results + """ + if addon.bool_setting("classic_search"): + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if result and result > -1: + self.search(result) + else: + T9Search(call=self.search, + start_value="", + history=self.__class__.__name__ + ".search") + if self.total_items > 0: + self.setFocusId(self.getCurrentContainerId()) + + @ch.action("parentdir", "*") + @ch.action("parentfolder", "*") + def previous_menu(self, control_id): + """ + close the currently active dialog + can also get overridden by skinners per control by setting a window prop + """ + onback = self.getProperty("%i_onback" % control_id) + if onback: + xbmc.executebuiltin(onback) + else: + self.close() + + @ch.action("previousmenu", "*") + def exit_script(self, control_id): + """ + completely exit script + """ + self.exit() + + @ch.action("left", "*") + @ch.action("right", "*") + @ch.action("up", "*") + @ch.action("down", "*") + def save_position(self, control_id): + """ + save position for any navigation keypress + """ + self.position = self.getCurrentListPosition() + + @ch.info("video") + # @ch.click_by_type("movie") + def open_video(self, control_id): + self.close() + xbmcgui.Dialog().info(self.FocusedItem(control_id)) + self.doModal() + + def search(self, label): + """ + set search string and update the container + """ + if not label: + return None + self.search_str = label + self.filters = [] + busy.disable() + self.reset("search") + busy.enable() + + def set_filter_label(self): + """ + build filter label for UI based on active filters + """ + filters = [] + for item in self.filters: + filter_label = item["label"].replace("|", " | ").replace(",", " + ") + filters.append("[COLOR FFAAAAAA]%s:[/COLOR] %s" % (item["typelabel"], filter_label)) + self.filter_label: str = " - ".join(filters) + + def update_content(self, force_update=False): + """ + fetch listitems and pagination info based on current state + """ + self.data = self.fetch_data(force=force_update) + if not self.data: + return None + self.listitems = self.data + self.total_pages = self.data.total_pages + self.total_items = self.data.totals + self.next_page_token = self.data.next_page_token + self.prev_page_token = self.data.prev_page_token + + def update_ui(self): + """ + add listitems to list, set focusposition, set window properties + """ + if not self.listitems and self.getFocusId() == self.getCurrentContainerId(): + self.setFocusId(ID_BUTTON_SEARCH) + self.clearList() + if self.listitems: + items = [] + for item in self.listitems: + item.set_label2(self.LABEL2[self.sort](item)) + items.append(item.get_listitem()) + self.addItems(items) + if self.column is not None: + self.setCurrentListPosition(self.column) + # self.setContent(self.listitems.content_type) + self.setProperty("TotalPages", str(self.total_pages)) + self.setProperty("TotalItems", str(self.total_items)) + self.setProperty("CurrentPage", str(self.page)) + self.setProperty("Filter_Label", self.filter_label) + self.setProperty("Sort_Label", self.sort_label) + self.setProperty("ArrowDown", "True" if self.page != self.total_pages else "") + self.setProperty("ArrowUp", "True" if self.page > 1 else "") + self.setProperty("Order_Label", addon.LANG(584 if self.order == "asc" else 585)) + self.setProperty("Type", self.TRANSLATIONS[self.type]) + + def reset(self, mode="filter"): + """ + resets the container to its default mode and updates + """ + self.page = 1 + self.mode = mode + self.verify_sort() + self.update() + + def go_to_next_page(self): + """ + go to the next page (for paginated results) + """ + self.get_column() + if self.page < self.total_pages: + self.page += 1 + self.prev_page_token = self.page_token + self.page_token = self.next_page_token + self.update() + + def go_to_prev_page(self): + """ + go to the previous page (for paginated results) + """ + self.get_column() + if self.page > 1: + self.page -= 1 + self.next_page_token = self.page_token + self.page_token = self.prev_page_token + self.update() + + def get_column(self): + """ + save the column of the currently focused item + """ + col = xbmc.getInfoLabel("Container(%s).Column" % self.getCurrentContainerId()) + self.column = int(col) if col != "" else None + + @busy.set_busy + def update(self, force_update=False): + """ + complete refresh of both content and ui + """ + self.update_content(force_update=force_update) + self.update_ui() + + def choose_sort_method(self, sort_key): + """ + open dialog and let user choose sortmethod + returns True if sorthmethod changed + """ + listitems = list(self.SORTS[sort_key].values()) + sort_strings = list(self.SORTS[sort_key].keys()) + preselect = listitems.index(self.sort_label) if self.sort_label in listitems else -1 + index = xbmcgui.Dialog().select(heading=addon.LANG(32104), + list=listitems, + preselect=preselect) + if index == -1 or listitems[index] == self.sort_label: + return False + self.sort = sort_strings[index] + self.sort_label = listitems[index] + return True + + def choose_filter(self, filter_code, header, options): + """ + open dialog and let user choose filter from *options + filter gets removed in case value is empty + filter_code: filter code from API + options: list of tuples with 2 items each: first is value, second is label + """ + values = [i[0] for i in options] + labels = [i[1] for i in options] + index = xbmcgui.Dialog().select(heading=addon.LANG(header), + list=labels) + if index == -1: + return None + if not values[index]: + self.remove_filter(filter_code) + self.add_filter(key=filter_code, + value=values[index], + label=labels[index]) + + def toggle_filter(self, filter_code): + """ + automatically add / remove filter with *filter_code + """ + index = self.find_filter_position(filter_code) + if index > -1: + del self.filters[index] + else: + pass # add filter... + + def find_filter_position(self, filter_code): + """ + find position of specific filter in filter list + """ + for i, item in enumerate(self.filters): + if item["type"] == filter_code: + return i + return -1 + + def remove_filter(self, filter_code): + """ + remove filter with specific filter_code from filter list + """ + index = self.find_filter_position(filter_code) + if index > -1: + del self.filters[index] + self.reset() + + def add_filter(self, key, value, label, typelabel="", force_overwrite=False, reset=True): + """ + add a filter to the filter list + a filter consists of a key and value (for api call), and label as well as + typelabel for displaying in UI. + *reset updates the container after modifying filters + *force_overwrite is used for filters which do not support ANDing + """ + if not value: + return False + new_filter = {"id": str(value), + "type": key, + "typelabel": typelabel, + "label": label} + if new_filter in self.filters: + return False + index = self.find_filter_position(key) + if index == -1: + self.filters.append(new_filter) + if reset: + self.reset() + return None + if force_overwrite: + self.filters[index]["id"] = str(value) + self.filters[index]["label"] = str(label) + if reset: + self.reset() + return None + self.filters[index]["id"] += ",%s" % value + self.filters[index]["label"] += ",%s" % label + ids = self.filters[index]["id"].split(",") + labels = self.filters[index]["label"].split(",") + self.filters[index]["id"] = ",".join(list(set(ids))) + self.filters[index]["label"] = ",".join(list(set(labels))) + if reset: + self.reset() diff --git a/script.extendedinfo/resources/kutil131/favs.py b/script.extendedinfo/resources/kutil131/favs.py new file mode 100644 index 000000000..ce5946fb6 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/favs.py @@ -0,0 +1,82 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +from resources.kutil131 import ItemList, ListItem, kodijson, utils + + +def get_favs_by_type(fav_type): + """ + returns dict list containing favourites with type *fav_type + """ + return [fav for fav in get_favs() if fav["type"] == fav_type] + + +def get_fav_path(fav): + """ + get builtin for fav according to type + *fav: dict from JSON API representing a favourite + """ + if fav["type"] == "media": + return "PlayMedia(%s)" % (fav["path"]) + elif fav["type"] == "script": + return "RunScript(%s)" % (fav["path"]) + elif "window" in fav and "windowparameter" in fav: + return "ActivateWindow(%s,%s)" % (fav["window"], fav["windowparameter"]) + else: + utils.log("error parsing favs") + + +def get_favs(): + """ + returns dict list containing favourites + """ + items = ItemList() + data = kodijson.get_favourites() + if "result" not in data or data["result"]["limits"]["total"] == 0: + return [] + for fav in data["result"]["favourites"]: + path = get_fav_path(fav) + item = ListItem(label=fav["title"], + path="plugin://script.extendedinfo/?info=action&&id=" + path) + item.set_artwork({'thumb': fav["thumbnail"]}) + item.set_properties({'type': fav["type"], + 'builtin': path}) + items.append(item) + return items + + +def get_icon_panel(number): + """ + get icon panel with index *number, returns dict list based on skin strings + """ + items = ItemList() + offset = number * 5 - 5 + for i in range(1, 6): + infopanel_path = utils.get_skin_string("IconPanelItem%i.Path" % (i + offset)) + item = ListItem(label=utils.get_skin_string("IconPanelItem%i.Label" % (i + offset)), + path="plugin://script.extendedinfo/?info=action&&id=" + infopanel_path) + item.set_artwork({'thumb': utils.get_skin_string("IconPanelItem%i.Icon" % (i + offset))}) + item.set_properties({'type': utils.get_skin_string("IconPanelItem%i.Type" % (i + offset)), + 'id': "IconPanelitem%i" % (i + offset)}) + items.append(item) + return items + + +def get_addons_by_author(author_name, installed="all"): + """ + get a list of addons from *author_name + *installed: "all", True, False + """ + repo_addons = kodijson.get_addons(installed=installed, + properties=["installed", "author", "name", "thumbnail", "fanart"]) + addons = [item for item in repo_addons if item["author"] == author_name] + items = ItemList() + for item in addons: + path = 'InstallAddon(%s)' % item["id"] + item = ListItem(label=item["name"], + path="plugin://script.extendedinfo/?info=action&&id=" + path) + item.set_artwork({'thumb': item["thumbnail"], + "fanart": item["fanart"]}) + item.set_properties({'installed': item["installed"]}) + items.append(item) + return items diff --git a/script.extendedinfo/resources/kutil131/imagetools.py b/script.extendedinfo/resources/kutil131/imagetools.py new file mode 100644 index 000000000..9027ae336 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/imagetools.py @@ -0,0 +1,114 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import os +import threading +import urllib.parse + +import PIL.Image +import PIL.ImageFilter +import xbmc +import xbmcvfs + +from resources.kutil131 import addon, utils + +THUMBS_CACHE_PATH = utils.translate_path("special://profile/Thumbnails/Video") +IMAGE_PATH = os.path.join(addon.DATA_PATH, "images") + + +def blur(input_img, radius=25): + if not input_img: + return {} + if not xbmcvfs.exists(IMAGE_PATH): + xbmcvfs.mkdir(IMAGE_PATH) + #input_img = utils.translate_path(urllib.parse.unquote(input_img)) + # input_img = input_img.replace("image://video@", "").rstrip("/") not working + #input_img = input_img.rstrip("/") + cachedthumb = xbmc.getCacheThumbName(input_img) + filename = "%s-radius_%i.png" % (cachedthumb, radius) + targetfile = os.path.join(IMAGE_PATH, filename) + vid_cache_file = os.path.join("special://profile/Thumbnails/Video", cachedthumb[0], cachedthumb) + cache_file = os.path.join("special://profile/Thumbnails", cachedthumb[0], cachedthumb[:-4] + ".jpg") + if xbmcvfs.exists(targetfile): + img = PIL.Image.open(targetfile) + return {"ImageFilter": targetfile, + "ImageColor": get_colors(img)} + try: + if xbmcvfs.exists(cache_file): + img = PIL.Image.open(utils.translate_path(cache_file)) + elif xbmcvfs.exists(vid_cache_file): + img = PIL.Image.open(utils.translate_path(vid_cache_file)) + else: + xbmcvfs.copy(input_img, targetfile) + img = PIL.Image.open(targetfile) + img.thumbnail((200, 200), PIL.Image.ANTIALIAS) + imgfilter = MyGaussianBlur(radius=radius) + img = img.convert('RGB').filter(imgfilter) + img.save(targetfile) + except Exception: + utils.log("Could not get image for %s" % input_img) + return {} + return {"ImageFilter": targetfile, + "ImageColor": get_colors(img)} + + +def get_cached_thumb(filename): + if filename.startswith("stack://"): + filename = filename[8:].split(" , ")[0] + cachedthumb = xbmc.getCacheThumbName(filename) + if not filename.endswith("folder.jpg"): + if ".jpg" in filename: + cachedthumb = cachedthumb.replace("tbn", "jpg") + elif ".png" in filename: + cachedthumb = cachedthumb.replace("tbn", "png") + return os.path.join(THUMBS_CACHE_PATH, cachedthumb[0], cachedthumb).replace("/Video", "") + + +def get_colors(img): + width, height = img.size + try: + pixels = img.load() + except Exception: + return "FFF0F0F0" + data = [] + for x in range(width // 2): + data += [pixels[x * 2, y * 2] for y in range(height // 2)] + pix_values = [(x[0], x[1], x[2]) for x in data if 150 < (x[0] + x[1] + x[2]) < 720] + if len(pix_values) == 0: + return "FFF0F0F0" + r_avg = int(sum([i[0] for i in pix_values]) / len(pix_values)) + g_avg = int(sum([i[1] for i in pix_values]) / len(pix_values)) + b_avg = int(sum([i[2] for i in pix_values]) / len(pix_values)) + avg = (r_avg + g_avg + b_avg) / 3 + min_brightness = 170 + if avg < min_brightness: + diff = min_brightness - avg + r_avg = int(min(r_avg + diff, 255)) + g_avg = int(min(g_avg + diff, 255)) + b_avg = int(min(b_avg + diff, 255)) + return f"FF{r_avg:02X}{g_avg:02X}{b_avg:02X}" + + +class FilterImageThread(threading.Thread): + + def __init__(self, image="", radius=25): + super().__init__() + self.image = image + self.radius = radius + self.info = {} + + def run(self): + try: + self.info = blur(self.image, self.radius) + except Exception: + pass + + +class MyGaussianBlur(PIL.ImageFilter.Filter): + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = radius + + def filter(self, image): + return image.gaussian_blur(self.radius) diff --git a/script.extendedinfo/resources/kutil131/itemlist.py b/script.extendedinfo/resources/kutil131/itemlist.py new file mode 100644 index 000000000..f77e78574 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/itemlist.py @@ -0,0 +1,202 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmcplugin + +from resources.kutil131 import addon, utils + +SORTS = {"none": xbmcplugin.SORT_METHOD_NONE, + "unsorted": xbmcplugin.SORT_METHOD_UNSORTED, + "label": xbmcplugin.SORT_METHOD_LABEL, + "label_ignore_the": xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE, + "label_ignore_folders": xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS, + "date": xbmcplugin.SORT_METHOD_DATE, + "size": xbmcplugin.SORT_METHOD_SIZE, + "file": xbmcplugin.SORT_METHOD_FILE, + "drive_type": xbmcplugin.SORT_METHOD_DRIVE_TYPE, + "tracknum": xbmcplugin.SORT_METHOD_TRACKNUM, + "duration": xbmcplugin.SORT_METHOD_DURATION, + "video_title": xbmcplugin.SORT_METHOD_VIDEO_TITLE, + "title": xbmcplugin.SORT_METHOD_TITLE, + "title_ignore_the": xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE, + "artist": xbmcplugin.SORT_METHOD_ARTIST, + "artist_ignore_the": xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE, + "album": xbmcplugin.SORT_METHOD_ALBUM, + "album_ignore_the": xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE, + "genre": xbmcplugin.SORT_METHOD_GENRE, + "year": xbmcplugin.SORT_METHOD_VIDEO_YEAR, + "rating": xbmcplugin.SORT_METHOD_VIDEO_RATING, + "program_count": xbmcplugin.SORT_METHOD_PROGRAM_COUNT, + "playlist_order": xbmcplugin.SORT_METHOD_PLAYLIST_ORDER, + "episode": xbmcplugin.SORT_METHOD_EPISODE, + "sorttitle": xbmcplugin.SORT_METHOD_VIDEO_SORT_TITLE, + "sorttitle_ignore_the": xbmcplugin.SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE, + "productioncode": xbmcplugin.SORT_METHOD_PRODUCTIONCODE, + "song_rating": xbmcplugin.SORT_METHOD_SONG_RATING, + "mpaa": xbmcplugin.SORT_METHOD_MPAA_RATING, + "video_runtime": xbmcplugin.SORT_METHOD_VIDEO_RUNTIME, + "studio": xbmcplugin.SORT_METHOD_STUDIO, + "studio_ignore_the": xbmcplugin.SORT_METHOD_STUDIO_IGNORE_THE, + "bitrate": xbmcplugin.SORT_METHOD_BITRATE, + "listeners": xbmcplugin.SORT_METHOD_LISTENERS, + "country": xbmcplugin.SORT_METHOD_COUNTRY, + "dateadded": xbmcplugin.SORT_METHOD_DATEADDED, + "fullpath": xbmcplugin.SORT_METHOD_FULLPATH, + "lastplayed": xbmcplugin.SORT_METHOD_LASTPLAYED, + "playcount": xbmcplugin.SORT_METHOD_PLAYCOUNT, + "channel": xbmcplugin.SORT_METHOD_CHANNEL, + "date_taken": xbmcplugin.SORT_METHOD_DATE_TAKEN, + "userrating": xbmcplugin.SORT_METHOD_VIDEO_USER_RATING} + + +class ItemList: + + def __init__(self, items=None, content_type: str ="", name="", sorts=None, totals=None, properties=None): + self.name = name + self.content_type: str = content_type + self.totals = totals + self.total_pages = 1 + self._items = items if items else [] + self.sorts = sorts if sorts else [] + self._properties = properties if properties else [] + self.local_first = True + self.sortkey = False + self.next_page_token = None + self.prev_page_token = None + + def __len__(self): + return len(self._items) + + def __iter__(self): + return iter(self._items) + + def __getitem__(self, key): + return self._items[key] + + def __cmp__(self, other): + return len(self) - len(other) + + def __bool__(self): + return len(self._items) > 0 + + def __repr__(self): + return "Itemlist with length %s. Content type: %s" % (len(self._items), self.content_type) + + def __add__(self, other): + return ItemList(items=self._items + list(other.items()), + content_type=self.content_type, + name=self.name, + sorts=self.sorts, + properties=self._properties) + + def __iadd__(self, other): + self._items += list(other.items()) + return self + + def prettify(self): + """ + log a formatted list of all items + """ + for item in self._items: + utils.log(item) + + def items(self): + return self._items + + def append(self, item): + self._items.append(item) + + def remove(self, index): + self._items.remove(index) + + def set_name(self, name): + self.name = name + + def set_content(self, content_type): + self.content_type = content_type + + def set_totals(self, totals): + self.totals = totals + + def set_total_pages(self, total_pages): + self.total_pages = total_pages + + def set_sorts(self, sorts): + self.sorts = sorts + + def set_properties(self, properties): + self._properties = properties + + def update_properties(self, properties): + self._properties.update({k: v for k, v in properties.items() if v}) + + def set_property(self, key, value): + self._properties[key] = value + + def get_property(self, key): + value = self._properties.get(key) + return value if value else "" + + def create_listitems(self): + """ + returns list with xbmcgui listitems + """ + return [item.get_listitem() for item in self._items] if self._items else [] + + def set_plugin_list(self, handle): + """ + populate plugin list with *handle, set sorts and content + """ + # these fixed sortmethods are only temporary + if self.content_type == "tvshows": + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_RATING) + elif self.content_type == "episodes": + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_RATING) + elif self.content_type == "movies": + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(handle, xbmcplugin.SORT_METHOD_VIDEO_RATING) + for item in self.sorts: + xbmcplugin.addSortMethod(handle, SORTS[item]) + if self.content_type: + xbmcplugin.setContent(handle, self.content_type) + items = [(i.get_path(), i.get_listitem(), i.is_folder()) for i in self._items] + xbmcplugin.addDirectoryItems(handle=handle, + items=items, + totalItems=len(items)) + xbmcplugin.setPluginFanart(handle, addon.FANART) + xbmcplugin.endOfDirectory(handle) + + def sort(self): + """ + sort based on self.sortkey and self.localfirst + """ + self._items = sorted(self._items, + key=lambda k: k.get_info(self.sortkey), + reverse=True) + if self.local_first: + local_items = [i for i in self._items if i.get_info("dbid")] + remote_items = [i for i in self._items if not i.get_info("dbid")] + self._items = local_items + remote_items + + def reduce(self, key="job"): + """ + remove duplicate listitems (based on property "id") and merge their property with key *key + """ + ids = [] + merged_items = [] + for item in self._items: + id_ = item.get_property("id") + if id_ not in ids: + ids.append(id_) + merged_items.append(item) + else: + index = ids.index(id_) + if key in merged_items[index]: + merged_items[index][key] = merged_items[index][key] + " / " + str(item[key]) + self._items = merged_items + return self diff --git a/script.extendedinfo/resources/kutil131/kodiaddon.py b/script.extendedinfo/resources/kutil131/kodiaddon.py new file mode 100644 index 000000000..950f3b38f --- /dev/null +++ b/script.extendedinfo/resources/kutil131/kodiaddon.py @@ -0,0 +1,139 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import base64 +import hashlib +import os +import uuid + +import xbmc +import xbmcaddon +import xbmcgui +import xbmcvfs + +HOME = xbmcgui.Window(10000) + + +class Addon: + """ + Wrapper for xbmcaddon.Addon() + """ + + def __init__(self, *args, **kwargs): + self.addon = xbmcaddon.Addon(*args) + self.ID = self.addon.getAddonInfo('id') + self.ICON = self.addon.getAddonInfo('icon') + self.NAME = self.addon.getAddonInfo('name') + self.FANART = self.addon.getAddonInfo('fanart') + self.AUTHOR = self.addon.getAddonInfo('author') + self.CHANGELOG = self.addon.getAddonInfo('changelog') + self.DESCRIPTION = self.addon.getAddonInfo('description') + self.DISCLAIMER = self.addon.getAddonInfo('disclaimer') + self.VERSION = self.addon.getAddonInfo('version') + self.PATH = self.addon.getAddonInfo('path') + self.PROFILE = self.addon.getAddonInfo('profile') + self.SUMMARY = self.addon.getAddonInfo('summary') + self.TYPE = self.addon.getAddonInfo('type') + self.MEDIA_PATH = os.path.join(self.PATH, "resources", "skins", "Default", "media") + self.DATA_PATH = xbmcvfs.translatePath("special://profile/addon_data/%s" % self.ID) + + def setting(self, setting_name): + """ + get setting with name *setting_name + """ + return self.addon.getSetting(setting_name) + + def set_setting(self, setting_name, string): + """ + set setting with name *setting_name to value *string + """ + self.addon.setSetting(str(setting_name), str(string)) + + def set_password_prompt(self, setting_name): + password = xbmcgui.Dialog().input(self.LANG(12326), option=xbmcgui.ALPHANUM_HIDE_INPUT) + if password: + self.set_password(setting_name, password) + + def set_password(self, setting_name, string): + self.addon.setSetting(setting_name, encode_string(string)) + + def get_password(self, setting_name): + mac = str(uuid.getnode()) + mac_hash = hashlib.md5(mac.encode()).hexdigest() + if not self.addon.getSetting("mac_hash"): + self.addon.setSetting("mac_hash", mac_hash) + elif self.addon.getSetting("mac_hash") != mac_hash: + xbmcgui.Dialog().notification("Error", "MAC id changed. Please enter password again in settings.") + self.addon.setSetting("mac_hash", mac_hash) + return None + setting = self.addon.getSetting(setting_name) + if setting: + return decode_string(setting) + + def bool_setting(self, setting_name): + """ + get bool setting (either True or False) + """ + return self.addon.getSetting(setting_name) == "true" + + def reload_addon(self): + self.addon = xbmcaddon.Addon(self.ID) + + def LANG(self, id_): + return self.addon.getLocalizedString(id_) if 31000 <= id_ <= 33000 else xbmc.getLocalizedString(id_) + + def set_global(self, setting_name: str, setting_value: str) ->None: + """sets xbmc Window SetProperty(setting_name,setting_value,home) + + Args: + setting_name (str): property key + setting_value (str): property value + """ + HOME.setProperty(setting_name, setting_value) + + def get_global(self, setting_name): + return HOME.getProperty(setting_name) + + def clear_global(self, setting_name): + HOME.clearProperty(setting_name) + + def clear_globals(self): + HOME.clearProperties() + + +def encode_string(clear: str) -> str: + """base64 encodes a string + + Args: + clear (str): string to be encoded + + Returns: + str: the url safe base64 encoding as bytes + """ + enc = [] + key = str(uuid.getnode()) + clear_enc = clear.encode() + for i, ele in enumerate(clear_enc): + key_c = key[i % len(key)] + enc_c = chr((ele + ord(key_c)) % 256) + enc.append(enc_c) + return base64.urlsafe_b64encode("".join(enc).encode()).decode() + + +def decode_string(enc: str, uuick: str='') -> str: + """return decoded string (encoded with uuid) + + Args: + enc (str): base64 string encoded by encode_string + + Returns: + str: the decoded string + """ + dec = [] + key = str(uuid.getnode()) if not uuick else uuick + enc = base64.urlsafe_b64decode(enc.encode()).decode() + for i, ele in enumerate(enc): + key_c = key[i % len(key)] + dec_c = ((256 + ord(ele) - ord(key_c)) % 256).to_bytes(1, 'little') + dec.append(dec_c) + return bytes.join(b'', dec).decode() diff --git a/script.extendedinfo/resources/kutil131/kodijson.py b/script.extendedinfo/resources/kutil131/kodijson.py new file mode 100644 index 000000000..44233df12 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/kodijson.py @@ -0,0 +1,134 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import json +import xbmc + + +def play_media(media_type, dbid, resume=True): + ''' + Start playback of media item + *media_type: movie, episode, musicvideo, album, song + *dbid: DBID of media to play + *resume: Resume from last position (only movie/episode) + ''' + if media_type in ['movie', 'episode']: + return get_json(method="Player.Open", + params={"item": {"%sid" % media_type: int(dbid)}, "options": {"resume": resume}}) + elif media_type in ['musicvideo', 'album', 'song']: + return get_json(method="Player.Open", + params={"item": {"%sid" % media_type: int(dbid)}}) + + +def get_directory(path, media_type="files"): + """ + get list with items from directory *path. + """ + return get_json(method="Files.GetDirectory", + params={"directory": path, "media": media_type}) + + +def send_text(text, close_keyboard=True): + """ + SendText JSON message + """ + return get_json(method="Input.SendText", + params={"text": text, "done": "true" if close_keyboard else "false"}) + + +def get_artists(properties=None): + """ + return list of artists from database + """ + properties = properties if properties else [] + data = get_json(method="AudioLibrary.GetArtists", + params={"properties": properties}) + if "result" in data and "artists" in data["result"]: + return data["result"]["artists"] + return [] + + +def get_addons(properties=None, installed=True, enabled="all"): + ''' + Get a list of addons + *properties: list of properties + *installed: True, False or "all" + *enabled: True, False or "all" + ''' + params = {"properties": properties if properties else [], + "installed": installed, + "enabled": enabled} + data = get_json(method="Addons.GetAddons", + params=params) + if "result" in data and "addons" in data["result"]: + return data["result"]["addons"] + return [] + + +def get_movies(properties=None): + ''' + Get a list of movies + *properties: list of properties + ''' + properties = properties if properties else [] + data = get_json(method="VideoLibrary.GetMovies", + params={"properties": properties}) + if "result" in data and "movies" in data["result"]: + return data["result"]["movies"] + return [] + + +def get_tvshows(properties=None): + ''' + Get a list of TvShows + *properties: list of properties + ''' + properties = properties if properties else [] + data = get_json(method="VideoLibrary.GetTVShows", + params={"properties": properties}) + if "result" in data and "tvshows" in data["result"]: + return data["result"]["tvshows"] + return [] + + +def set_userrating(media_type, dbid, rating): + ''' + Set the userrating for media items + *media_type: movie, tv or episode + *dbid: DBID of media to get rated + *rating: Actual rating value: 1-10 + ''' + if media_type == "movie": + return get_json(method="VideoLibrary.SetMovieDetails", + params={"movieid": dbid, "userrating": rating}) + elif media_type == "tv": + return get_json(method="VideoLibrary.SetTVShowDetails", + params={"tvshowid": dbid, "userrating": rating}) + elif media_type == "episode": + return get_json(method="VideoLibrary.SetEpisodeDetails", + params={"episodeid": dbid, "userrating": rating}) + + +def get_favourites(): + """ + get list with favourites + """ + return get_json(method="Favourites.GetFavourites", + params={"type": None, "properties": ["path", "thumbnail", "window", "windowparameter"]}) + + +def set_art(media_type, art, dbid): + """ + set artwork via json + """ + return get_json(method="VideoLibrary.Set%sDetails" % media_type, + params={"art": art, + "%sid" % media_type.lower(): int(dbid)}) + + +def get_json(method, params): + """ + communicate with kodi JSON-RPC + """ + json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "%s", "params": %s, "id": 1}' % (method, json.dumps(params))) + return json.loads(json_query) diff --git a/script.extendedinfo/resources/kutil131/kodilogging.py b/script.extendedinfo/resources/kutil131/kodilogging.py new file mode 100644 index 000000000..2f925189d --- /dev/null +++ b/script.extendedinfo/resources/kutil131/kodilogging.py @@ -0,0 +1,57 @@ +# +# Copyright (C) 2015 Thomas Amland +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + + +import logging + +import xbmc +import xbmcaddon + +from resources.kutil131 import addon + + +class KodiLogHandler(logging.StreamHandler): + + def __init__(self): + logging.StreamHandler.__init__(self) + addon_id = xbmcaddon.Addon().getAddonInfo('id') + prefix = b"[%s] " % addon_id + formatter = logging.Formatter(prefix + b'%(name)s: %(message)s') + self.setFormatter(formatter) + + def emit(self, record): + levels = { + logging.CRITICAL: xbmc.LOGFATAL, + logging.ERROR: xbmc.LOGERROR, + logging.WARNING: xbmc.LOGWARNING, + logging.INFO: xbmc.LOGINFO, + logging.DEBUG: xbmc.LOGDEBUG, + logging.NOTSET: xbmc.LOGNONE, + } + if addon.bool_setting('debug'): + try: + xbmc.log(self.format(record), levels[record.levelno]) + except UnicodeEncodeError: + xbmc.log(self.format(record), levels[record.levelno]) + + def flush(self): + pass + +def config(): + logger = logging.getLogger() + logger.addHandler(KodiLogHandler()) + logger.setLevel(logging.DEBUG) diff --git a/script.extendedinfo/resources/kutil131/listitem.py b/script.extendedinfo/resources/kutil131/listitem.py new file mode 100644 index 000000000..8f36ed596 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/listitem.py @@ -0,0 +1,581 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +from __future__ import annotations + +import xbmc +import xbmcgui + +from resources.kutil131 import utils + +INFOTAG_DISPATCHER = { + 'genre':'setGenres', + 'country':'setCountries', + 'year':'setYear', + 'episode':'setEpisode', + 'season':'setSeason', + 'sortepisode':'setSortEpisode', + 'sortseason':'setSortSeason', + 'episodeguide':'setEpisodeGuide', + 'showlink':'', + 'top250':'setTop250', + 'setid':'setSetId', + 'tracknumber':'setTrackNumber', + 'rating':'setRating', + 'userrating':'setUserRating', + 'watched':'', + 'playcount':'setPlaycount', + 'overlay':'', + 'cast':'setCast', + 'castandrole':'', + 'director':'setDirectors', + 'mpaa':'setMpaa', + 'plot': 'setPlot', + 'plotoutline':'setPlotOutline', + 'title':'setTitle', + 'originaltitle':'setOriginalTitle', + 'sorttitle':'setSortTitle', + 'duration':'setDuration', + 'studio':'setStudios', + 'tagline':'setTagLine', + 'writer':'setWriters', + 'tvshowtitle':'setTvShowTitle', + 'premiered':'setPremiered', + 'status':'setTvShowStatus', + 'set':'setSet', + 'setoverview':'setSetOverview', + 'tag':'setTags', + 'imdbnumber':'setUniqueID', + 'code':'setProductionCode', + 'aired':'setFirstAired', + 'credits':'', + 'lastplayed':'setLastPlayed', + 'album':'setAlbum', + 'artist':'setArtists', + 'votes':'setVotes', + 'path':'setPath', + 'trailer':'setTrailer', + 'dateadded':'setDateAdded', + 'mediatype':'setMediaType', + 'dbid':'setDbId' + } +KODI_MATRIX: bool = float(xbmc.getInfoLabel('System.BuildVersionCode')[:4]) < 19.9 + +class ListItem: + ICON_OVERLAY_NONE = 0 # No overlay icon + ICON_OVERLAY_RAR = 1 # Compressed *.rar files + ICON_OVERLAY_ZIP = 2 # Compressed *.zip files + ICON_OVERLAY_LOCKED = 3 # Locked files + ICON_OVERLAY_UNWATCHED = 4 # For not watched files + ICON_OVERLAY_WATCHED = 5 # For seen files + ICON_OVERLAY_HD = 6 # Is on hard disk stored + + def __init__(self, label="", label2="", path="", infos=None, properties=None, size="", artwork=None, ratings=None, ids=None): + """ + Kodi listitem, based on built-in datatypes + """ + # + # Define all instance variables + # + self.size = "" + self.videoinfo = [] + self.audioinfo = [] + self.subinfo = [] + self.cast = [] + self.specials = {} + self._is_folder = False + self.type: str = "video" + self.label: str = "" + self.label2 = "" + + self.set_label(label) + self.set_label2(label2) + self.path = path + self._properties = properties if properties else {} + self._artwork = artwork if artwork else {} + self._ratings: dict[str, str] = ratings if ratings else [] + self._ids = ids if ids else {} + self._infos = infos if infos else {} + + def __setitem__(self, key, value): + self._properties[key] = value + + def __getitem__(self, key): + if key in self._properties: + return self._properties[key] + elif key in self._artwork: + return self._artwork[key] + elif key in self._infos: + return self._infos[key] + elif key == "properties": + return self._properties + elif key == "infos": + return self._infos + elif key == "artwork": + return self._artwork + elif key == "label": + return self.label + elif key == "label2": + return self.label2 + elif key == "path": + return self.path + else: + raise KeyError(str(key)) + + def __repr__(self): + return "\n".join(["Label:", self.label, + "Label2:", self.label2, + "Path:", self.path, + "InfoLabels:", utils.dump_dict(self._infos), + "Properties:", utils.dump_dict(self._properties), + "Artwork:", utils.dump_dict(self._artwork), + "Specials:", utils.dump_dict(self.specials), + "", ""]) + + def __contains__(self, key): + if key in self._properties: + return True + elif key in self._artwork: + return True + elif key in self._infos: + return True + elif key in ["properties", "infos", "artwork", "label", "label2", "path"]: + return True + + def __delitem__(self, key): + if key in self._properties: + del self._properties[key] + + def get(self, key, fallback=None): + try: + return self.__getitem__(key) + except KeyError: + return fallback + + def update_from_listitem(self, listitem): + if not listitem: + return None + self.set_label(listitem.label) + self.set_label2(listitem.label2) + self.set_size(listitem.size) + self.update_properties(listitem.get_properties()) + self.update_artwork(listitem.get_artwork()) + self.update_infos(listitem.get_infos()) + + def set_label(self, label): + self.label = label + + def set_label2(self, label): + self.label2 = label + + def set_mimetype(self, mimetype): + self.specials["mimetype"] = mimetype + + def fix_at_top(self): + self.specials["specialsort"] = "top" + + def fix_at_bottom(self): + self.specials["specialsort"] = "bottom" + + def set_startoffset(self, value): + self.specials["startoffset"] = value + + def set_totaltime(self, value): + self.specials["totaltime"] = value + + def set_resumetime(self, value): + self.specials["resumetime"] = value + + def set_playable(self, value): + self.specials["isPlayable"] = value + + def is_playable(self): + return bool(self.specials.get("isPlayable")) + + def set_folder(self, value): + self._is_folder = value + + def is_folder(self): + return bool(self._is_folder) + +# playlist_starting_track isspecial item_start isplayable + + def set_size(self, size): + self.size = size + + def set_visible(self, condition): + self.specials["node.visible"] = condition + + def set_target(self, target): + self.specials["node.target"] = target + + def set_infos(self, infos): + self._infos = infos + + def set_artwork(self, artwork): + self._artwork = artwork + + def set_properties(self, properties): + self._properties = properties + + def update_properties(self, properties): + self._properties.update({k: v for k, v in properties.items() if v}) + + def update_artwork(self, artwork): + self._artwork.update({k: v for k, v in artwork.items() if v}) + + def update_infos(self, infos): + self._infos.update({k: v for k, v in infos.items() if v}) + + def set_art(self, key, value): + self._artwork[key] = value + + def set_property(self, key, value): + self._properties[key] = value + + def set_info(self, key, value): + self._infos[key] = value + + def get_path(self): + return self.path + + def get_art(self, key): + value = self._artwork.get(key) + return value if value else "" + + def get_info(self, key): + value = self._infos.get(key) + return value if value else "" + + def get_property(self, key): + value = self._properties.get(key) + return value if value else "" + + def get_artwork(self): + return {k: v for k, v in self._artwork.items() if v} + + def get_infos(self): + return {k: v for k, v in self._infos.items() if v} + + def get_properties(self): + return {k: v for k, v in self._properties.items() if v} + + def get_listitem(self) -> xbmcgui.ListItem: + #listitem: xbmcgui.ListItem + listitem = xbmcgui.ListItem(label=str(self.label) if self.label else "", + label2=str(self.label2) if self.label2 else "", + path=self.path) + props = {k: str(v) for k, v in self._properties.items() if v} + infos = {k.lower(): v for k, v in self._infos.items() if v} + infos["path"] = self.path + if "media_type" in infos: + infos["mediatype"] = infos["media_type"] + infos.pop("media_type") + if "file" in infos: + #infos["path"] = infos["file"] + infos.pop("file") + if "duration" in infos: + props['duration(h)'] = utils.format_time(infos["duration"], "h") + props['duration(m)'] = utils.format_time(infos["duration"], "m") + for key, value in props.items(): + listitem.setProperty(key, str(value)) + for key, value in self.specials.items(): + listitem.setProperty(key, str(value)) + artwork = {k: v for k, v in self._artwork.items() if v} + if artwork: + listitem.setArt(artwork) + if infos: + #Use setInfo for Matrix, Nexus complains so use videoinfo tag setters + if KODI_MATRIX: + listitem.setInfo(self.type, infos) + else: + vinfotag = listitem.getVideoInfoTag() + for k, v in infos.items(): + if k in ['genre','country','director','studio','writer']: v = v.split(' / ') + if k in ['year', 'votes', 'userrating']: v = int(v) + if k in ['file', 'media_type']: continue + getattr(vinfotag, INFOTAG_DISPATCHER[k])(v) + return listitem + + def to_windowprops(self, prefix="", window_id=10000): + window = xbmcgui.Window(window_id) + window.setProperty('%slabel' % (prefix), self.label) + window.setProperty('%slabel2' % (prefix), self.label2) + window.setProperty('%spath' % (prefix), self.path) + dct = utils.merge_dicts(self.get_properties(), + self.get_artwork(), + self.get_infos()) + for k, v in dct.items(): + window.setProperty('%s%s' % (prefix, k), str(v)) + + +class AudioItem(ListItem): + """ + Kodi audio listitem, based on built-in datatypes + """ + props = ["id", + "artist_instrument", + "artist_style", + "artist_mood", + "artist_born", + "artist_formed", + "artist_description", + "artist_genre", + "artist_died", + "artist_disbanded", + "artist_yearsactive", + "artist_born", + "artist_died", + "album_description", + "album_theme", + "album_mood", + "album_style", + "album_type", + "album_label", + "album_artist", + "album_genre", + "album_title", + "album_rating", + "album_userrating", + "album_votes", + "album_releasetype"] + + def __init__(self, *args, **kwargs): + self.type = "music" + super().__init__(*args, **kwargs) + + def from_listitem(self, listitem): + info = listitem.getAudioInfoTag() + self.label = listitem.getLabel() + self.path = info.getPath() + self._infos = {"dbid": info.GetDatabaseId(), + "mediatype": info.GetMediaType(), + "title": info.GetTitle(), + "votes": info.GetVotes(), + "rating": info.GetRating(), + "userrating": info.GetUserRating(), + "file": info.GetFile(), + "comment": info.getComment(), + "lyrics": info.getLyrics(), + "genre": info.getGenre(), + "lastplayed": info.getLastPlayed(), + "listeners": info.getListeners(), + "playcount": info.getPlayCount(), + "year": info.getReleaseDate()} + self._properties = {key: listitem.getProperty(key) for key in self.props} + + def from_infolabels(self): + self.label = xbmc.getInfoLabel("ListItem.Label") + self.path = xbmc.getInfoLabel("ListItem.Path") + self._infos = {"dbid": xbmc.getInfoLabel("ListItem.Label"), + "mediatype": xbmc.getInfoLabel("ListItem.DBType"), + "title": xbmc.getInfoLabel("ListItem.Title"), + "votes": xbmc.getInfoLabel("ListItem.Votes"), + "rating": xbmc.getInfoLabel("ListItem.Rating"), + "userrating": xbmc.getInfoLabel("ListItem.UserRating"), + "file": xbmc.getInfoLabel("ListItem.FileNameAndPath"), + "comment": xbmc.getInfoLabel("ListItem.Comment"), + "lyrics": xbmc.getInfoLabel("ListItem.Lyrics"), + "genre": xbmc.getInfoLabel("ListItem.Genre"), + "lastplayed": xbmc.getInfoLabel("ListItem.Label"), + "listeners": xbmc.getInfoLabel("ListItem.Listeners"), + "playcount": xbmc.getInfoLabel("ListItem.Playcount"), + "year": xbmc.getInfoLabel("ListItem.Year")} + self._properties = {key: xbmc.getInfoLabel("ListItem.Property({}".format(key)) for key in self.props} + + +class VideoItem(ListItem): + """Kodi video listitem, based on built-in datatypes + + Args: + ListItem (class): The kutils131 ListItem class + """ + + def __init__(self, *args, **kwargs): + self.type = "video" + super().__init__(*args, **kwargs) + + def __repr__(self): + baseinfo = super().__repr__() + return "\n".join([baseinfo, + "Cast:", utils.dump_dict(self.cast), + "VideoStreams:", utils.dump_dict(self.videoinfo), + "AudioStreams:", utils.dump_dict(self.audioinfo), + "Ratings:", utils.dump_dict(self._ratings), + "Ids:", utils.dump_dict(self._ids), + "Subs:", utils.dump_dict(self.subinfo), + "", ""]) + + def from_listitem(self, listitem: xbmcgui.ListItem): + """ + xbmcgui listitem -> kutils131 listitem + """ + info = listitem.getVideoInfoTag() + self.label = listitem.getLabel() + self.path = info.getPath() + for provider in {"tmdb", "imdb", "trakt"}: + self._ratings[provider] = listitem.getRating(provider) + self._infos = {"dbid": info.getDbId(), + "mediatype": info.getMediaType(), + "plot": info.getPlot(), + "plotoutline": info.getPlotOutline(), + "tvshowtitle": info.getTVShowTitle(), + "title": info.getTitle(), + "votes": info.getVotes(), + "season": info.getSeason(), + "episode": info.getEpisode(), + "rating": info.getRating(), + "userrating": info.getUserRating(), + "pictureurl": info.getPictureURL(), + "cast": info.getCast(), + "file": info.getFile(), + "trailer": info.getTrailer(), + "originaltitle": info.getOriginalTitle(), + "tagline": info.getTagLine(), + "genre": info.getGenre(), + "director": info.getDirector(), + "writer": info.getWritingCredits(), + "lastplayed": info.getLastPlayed(), + "premiered": info.getPremiered(), + "firstaired": info.getFirstAired(), + "playcount": info.getPlayCount(), + "imdbnumber": info.getIMDBNumber(), + "year": info.getYear()} + + def update_from_listitem(self, listitem: ListItem): + if not listitem: + return None + super().update_from_listitem(listitem) + self.set_videoinfos(listitem.videoinfo) + self.set_audioinfos(listitem.audioinfo) + self.set_subinfos(listitem.subinfo) + self.set_cast(listitem.cast) + + def get_listitem(self) -> xbmcgui.ListItem: + listitem = super().get_listitem() + #Use listitem for Matrix, Nexus complains so use videoinfo tag setters + if KODI_MATRIX: + for item in self.videoinfo: + listitem.addStreamInfo("video", item) + for item in self.audioinfo: + listitem.addStreamInfo("audio", item) + for item in self.subinfo: + listitem.addStreamInfo("subtitle", item) + for item in self._ratings: + listitem.setRating(item["type"], item["rating"], item["votes"], item["default"]) + listitem.setUniqueIDs(self._ids) + listitem.setCast(self.cast) + else: + vinfotag = listitem.getVideoInfoTag() + for item in self.videoinfo: + vstream = xbmc.VideoStreamDetail() + for k, v in item.items(): + if k == 'aspect': + vstream.setAspect(v) + elif k == 'codec': + vstream.setCodec(v) + elif k == 'duration': + vstream.setDuration(v) + elif k == 'height': + vstream.setHeight(v) + elif k == 'stereomode': + vstream.setStereoMode(v) + elif k == 'language': + vstream.setLanguage(v) + elif k == 'width': + vstream.setWidth(v) + for item in self.audioinfo: + astream = xbmc.AudioStreamDetail() + for k, v in item.items(): + if k == 'channels': + astream.setChannels(v) + elif k == 'codec': + astream.setCodec(v) + elif k == 'language': + astream.setLanguage(v) + for item in self.subinfo: + substream = xbmc.SubtitleStreamDetail() + for k, v in item.items(): + if k == 'language': + substream.setLanguage(v) + vinfotag.setUniqueIDs(self._ids) + if self.cast: + castlist: list = [] + for actorinfo in self.cast: + actor = xbmc.Actor(actorinfo['name']) + for k, v in actorinfo.items(): + if k == 'role': + actor.setRole(v) + elif k == 'order': + actor.setOrder(v) + elif k == 'thumbnail': + actor.setThumbnail(v) + castlist.append(actor) + # listitem.setCast(self.cast) + vinfotag.setCast(castlist) + return listitem + + def add_videoinfo(self, info): + self.videoinfo.append(info) + + def add_audioinfo(self, info): + self.audioinfo.append(info) + + def add_subinfo(self, info): + self.subinfo.append(info) + + def add_cast(self, value): + self.cast.append(value) + + def set_cast(self, value): + self.cast = value + + def get_cast(self): + return self.cast + + def set_videoinfos(self, infos): + self.videoinfo = infos + + def set_audioinfos(self, infos): + self.audioinfo = infos + + def set_subinfos(self, infos): + self.subinfo = infos + + def get_rating(self, provider): + for item in self._ratings: + if item["provider"] == provider.lower(): + return item + return None + + def get_ratings(self): + return self._ratings + + def add_rating(self, provider, rating, votes=None, default=None): + self._ratings.append({"provider": provider.lower(), + "rating": rating, + "votes": int(votes), + "default": bool(default)}) + + def set_id(self, provider, uid): + self._ids[provider] = uid + + def get_id(self): + return self._ids + + def movie_from_dbid(self, dbid): + from resources.kutil131.localdb import LocalDB + if not dbid: + return None + self.update_from_listitem(LocalDB(last_fm=None).get_movie(dbid)) + + +class GameItem(ListItem): + """ + Kodi game listitem, based on built-in datatypes + """ + + def __init__(self, *args, **kwargs): + self.type = "game" + super().__init__(*args, **kwargs) + diff --git a/script.extendedinfo/resources/kutil131/localdb.py b/script.extendedinfo/resources/kutil131/localdb.py new file mode 100644 index 000000000..b08a15e44 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/localdb.py @@ -0,0 +1,526 @@ +# Copyright (C) 2016 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import itertools +import json + +from resources.kutil131 import ItemList, VideoItem, addon, kodijson, utils +from resources.kutil131.abs_last_fm import AbstractLastFM + +PLUGIN_BASE = "plugin://script.extendedinfo/?info=" +MOVIE_PROPS = ["title", "genre", "year", "rating", "director", "trailer", + "tagline", "plot", "plotoutline", "originaltitle", "lastplayed", + "playcount", "writer", "studio", "mpaa", "cast", "country", + "imdbnumber", "runtime", "set", "showlink", "streamdetails", + "top250", "votes", "file", "sorttitle", + "resume", "setid", "dateadded", "tag", "art", "userrating", + "ratings", "uniqueid", "premiered"] +TV_PROPS = ["title", "genre", "year", "rating", "plot", + "studio", "mpaa", "cast", "playcount", "episode", + "imdbnumber", "premiered", "votes", "lastplayed", + "file", "originaltitle", + "sorttitle", "episodeguide", "season", "watchedepisodes", + "dateadded", "tag", "art", "userrating", "ratings", "uniqueid", "runtime"] + + +class LocalDB: + + def __init__(self, last_fm: AbstractLastFM, *args, **kwargs): + """ + + :param last_fm: reference to implementation of AbstractLastFM + :param args: + :param kwargs: + """ + self.info = {} + self.artists = [] + self.albums = [] + self.last_fm = last_fm + if last_fm is None: + self.last_fm = AbstractLastFM() + + def get_artists(self): + """ + get list of artists from db (limited metadata) + """ + self.artists = kodijson.get_artists(properties=["musicbrainzartistid", "thumbnail"]) + return self.artists + + def get_similar_artists(self, artist_id): + """ + get list of artists from db which are similar to artist with *artist_id + based on LastFM online data + """ + simi_artists = self.last_fm.get_similar_artists(artist_id) + if simi_artists is None: + utils.log('Last.fm didn\'t return proper response') + return None + if not self.artists: + self.artists = self.get_artists() + artists = ItemList(content_type="artists") + for simi_artist, kodi_artist in itertools.product(simi_artists, self.artists): + if kodi_artist['musicbrainzartistid'] and kodi_artist['musicbrainzartistid'] == simi_artist['mbid']: + artists.append(kodi_artist) + elif kodi_artist['artist'] == simi_artist['name']: + data = kodijson.get_json(method="AudioLibrary.GetArtistDetails", + params={"properties": ["genre", "description", "mood", "style", "born", "died", "formed", "disbanded", "yearsactive", "instrument", "fanart", "thumbnail"], "artistid": kodi_artist['artistid']}) + item = data["result"]["artistdetails"] + artwork = {"thumb": item['thumbnail'], + "fanart": item['fanart']} + artists.append({"label": item['label'], + "artwork": artwork, + "title": item['label'], + "genre": " / ".join(item['genre']), + "artist_description": item['description'], + "userrating": item['userrating'], + "born": item['born'], + "died": item['died'], + "formed": item['formed'], + "disbanded": item['disbanded'], + "yearsactive": " / ".join(item['yearsactive']), + "style": " / ".join(item['style']), + "mood": " / ".join(item['mood']), + "instrument": " / ".join(item['instrument']), + "librarypath": 'musicdb://artists/%s/' % item['artistid']}) + utils.log('%i of %i artists found in last.FM are in Kodi database' % (len(artists), len(simi_artists))) + return artists + + def get_similar_movies(self, dbid): + """ + get list of movies from db which are similar to movie with *dbid + based on metadata-centric ranking + """ + movie = kodijson.get_json(method="VideoLibrary.GetMovieDetails", + params={"properties": ["genre", "director", "country", "year", "mpaa"], "movieid": dbid}) + if "moviedetails" not in movie['result']: + return [] + comp_movie = movie['result']['moviedetails'] + genres = comp_movie['genre'] + data = kodijson.get_json(method="VideoLibrary.GetMovies", + params={"properties": ["genre", "director", "mpaa", "country", "year"], "sort": {"method": "random"}}) + if "movies" not in data['result']: + return [] + quotalist = [] + for item in data['result']['movies']: + item["mediatype"] = "movie" + diff = abs(int(item['year']) - int(comp_movie['year'])) + hit = 0.0 + miss = 0.00001 + quota = 0.0 + for genre in genres: + if genre in item['genre']: + hit += 1.0 + else: + miss += 1.0 + if hit > 0.0: + quota = float(hit) / float(hit + miss) + if genres and item['genre'] and genres[0] == item['genre'][0]: + quota += 0.3 + if diff < 3: + quota += 0.3 + elif diff < 6: + quota += 0.15 + if comp_movie['country'] and item['country'] and comp_movie['country'][0] == item['country'][0]: + quota += 0.4 + if comp_movie['mpaa'] and item['mpaa'] and comp_movie['mpaa'] == item['mpaa']: + quota += 0.4 + if comp_movie['director'] and item['director'] and comp_movie['director'][0] == item['director'][0]: + quota += 0.6 + quotalist.append((quota, item["movieid"])) + quotalist = sorted(quotalist, + key=lambda quota: quota[0], + reverse=True) + movies = ItemList(content_type="movies") + for i, list_movie in enumerate(quotalist): + if comp_movie['movieid'] is not list_movie[1]: + newmovie = self.get_movie(list_movie[1]) + movies.append(newmovie) + if i == 20: + break + return movies + + def get_movies(self, limit=10): + """ + get list of movies with length *limit from db + """ + data = kodijson.get_json(method="VideoLibrary.GetMovies", + params={"properties": MOVIE_PROPS, "limits": {"end": limit}}) + if "result" not in data or "movies" not in data["result"]: + return [] + return ItemList(content_type="movies", + items=[self.handle_movie(item) for item in data["result"]["movies"]]) + + def get_tvshows(self, limit=10): + """ + get list of tvshows with length *limit from db + """ + data = kodijson.get_json(method="VideoLibrary.GetTVShows", + params={"properties": TV_PROPS, "limits": {"end": limit}}) + if "result" not in data or "tvshows" not in data["result"]: + return [] + return ItemList(content_type="movies", + items=[self.handle_tvshow(item) for item in data["result"]["tvshows"]]) + + def handle_movie(self, movie:dict) -> VideoItem: + """ + convert movie data to listitems + """ + trailer = PLUGIN_BASE + "playtrailer&&dbid=%s" % movie['movieid'] + if addon.setting("infodialog_onclick") != "false": + path = PLUGIN_BASE + 'extendedinfo&&dbid=%s' % movie['movieid'] + else: + path = PLUGIN_BASE + 'playmovie&&dbid=%i' % movie['movieid'] + resume = movie['resume'] + if (resume['position'] and resume['total']) > 0: + resumable = "true" + played = int((float(resume['position']) / float(resume['total'])) * 100) + else: + resumable = "false" + played = 0 + db_movie = VideoItem(label=movie.get('label'), + path=path) + db_movie.set_infos({'title': movie.get('label'), + 'dbid': movie['movieid'], + 'file': movie.get('file'), + 'year': movie.get('year'), + 'premiered' : movie.get('premiered'), + 'tagline': movie.get('tagline'), + 'writer': " / ".join(movie['writer']), + 'mediatype': "movie", + 'set': movie.get("set"), + 'playcount': movie.get("playcount"), + 'setid': movie.get("setid"), + 'top250': movie.get("top250"), + 'imdbnumber': movie.get("uniqueid", {}).get("imdb", ""), + 'userrating': movie.get('userrating'), + 'trailer': trailer, + 'rating': round(float(movie['rating']), 1), + 'director': " / ".join(movie.get('director')), + #'writer': " / ".join(movie.get('writer')), + #"tag": " / ".join(movie['tag']), + 'tag': movie.get('tag'), + "genre": " / ".join(movie['genre']), + 'plot': movie.get('plot'), + 'plotoutline': movie.get('plotoutline'), + 'studio': " / ".join(movie.get('studio')), + 'mpaa': movie.get('mpaa'), + 'originaltitle': movie.get('originaltitle')}) + db_movie.set_properties({'imdb_id': movie.get('uniqueid').get('imdb', ''), + 'showlink': " / ".join(movie['showlink']), + 'set': movie.get("set"), + 'setid': movie.get("setid"), + 'percentplayed': played, + 'resume': resumable}) + db_movie.set_artwork(movie['art']) + db_movie.set_videoinfos(movie['streamdetails']["video"]) + db_movie.set_audioinfos(movie['streamdetails']["audio"]) + stream_info = media_streamdetails(movie['file'].lower(), + movie['streamdetails']) + db_movie.update_properties(stream_info) + db_movie.set_cast(movie.get("cast")) + return db_movie + + def handle_tvshow(self, tvshow): + """ + convert tvshow data to listitems + """ + if addon.setting("infodialog_onclick") != "false": + path = PLUGIN_BASE + 'extendedtvinfo&&dbid=%s' % tvshow['tvshowid'] + else: + path = PLUGIN_BASE + 'action&&id=ActivateWindow(videos,videodb://tvshows/titles/%s/,return)' % tvshow['tvshowid'] + db_tvshow = VideoItem(label=tvshow.get("label"), + path=path) + db_tvshow.set_infos({'title': tvshow.get('label'), + 'dbid': tvshow['tvshowid'], + 'genre': " / ".join(tvshow.get('genre')), + 'rating': round(float(tvshow['rating']), 1), + 'mediatype': "tvshow", + 'mpaa': tvshow.get("mpaa"), + 'plot': tvshow.get("plot"), + 'votes': tvshow.get("votes"), + 'studio': " / ".join(tvshow.get('studio')), + 'premiered': tvshow.get("premiered"), + 'playcount': tvshow.get("playcount"), + 'imdbnumber': tvshow.get("imdbnumber"), + 'userrating': tvshow.get("userrating"), + 'duration': tvshow.get("duration"), + # "tag": " / ".join(movie['tag']), + 'year': tvshow.get('year'), + 'originaltitle': tvshow.get('originaltitle')}) + db_tvshow.set_properties({'imdb_id': tvshow.get('imdbnumber'), + 'file': tvshow.get('file'), + 'watchedepisodes': tvshow.get('watchedepisodes'), + 'totalepisodes': tvshow.get('episode')}) + db_tvshow.set_artwork(tvshow['art']) + db_tvshow.set_cast(tvshow.get("cast")) + return db_tvshow + + def get_movie(self, movie_id) -> VideoItem: + """ + get info from db for movie with *movie_id + """ + response = kodijson.get_json(method="VideoLibrary.GetMovieDetails", + params={"properties": MOVIE_PROPS, "movieid": movie_id}) + if "result" in response and "moviedetails" in response["result"]: + return self.handle_movie(response["result"]["moviedetails"]) + return {} + + def get_tvshow(self, tvshow_id): + """ + get info from db for tvshow with *tvshow_id + """ + response = kodijson.get_json(method="VideoLibrary.GetTVShowDetails", + params={"properties": TV_PROPS, "tvshowid": tvshow_id}) + if "result" in response and "tvshowdetails" in response["result"]: + return self.handle_tvshow(response["result"]["tvshowdetails"]) + return {} + + def get_albums(self): + """ + get a list of all albums from db + """ + data = kodijson.get_json(method="AudioLibrary.GetAlbums", + params={"properties": ["title"]}) + if "result" not in data or "albums" not in data['result']: + return [] + return data['result']['albums'] + + def get_compare_info(self, media_type="movie", items=None): + """ + fetches info needed for the locally-available check + Caches some info as window properties. + Hacky, but by far fastest way to cache between sessions + """ + infos = ["ids", "imdbs", "otitles", "titles"] + if not self.info.get(media_type): + self.info[media_type] = {} + dct = self.info[media_type] + # now = time.time() + dct["ids"] = addon.get_global("%s_ids.JSON" % media_type) + if dct["ids"] and dct["ids"] != "[]": + dct["ids"] = json.loads(dct["ids"]) + dct["otitles"] = json.loads(addon.get_global("%s_otitles.JSON" % media_type)) + dct["titles"] = json.loads(addon.get_global("%s_titles.JSON" % media_type)) + dct["imdbs"] = json.loads(addon.get_global("%s_imdbs.JSON" % media_type)) + else: + for info in infos: + dct[info] = [] + for item in items: + dct["ids"].append(item["%sid" % media_type]) + dct["imdbs"].append(item["imdbnumber"]) + dct["otitles"].append(item["originaltitle"].lower()) + dct["titles"].append(item["label"].lower()) + for info in infos: + addon.set_global("%s_%s.JSON" % (media_type, info), json.dumps(dct[info])) + + self.info[media_type] = dct + + def merge_with_local(self, media_type, items, library_first=True, sortkey=False): + """ + merge *items from online sources with local db info (and sort) + works for movies and tvshows + """ + get_list = kodijson.get_movies if media_type == "movie" else kodijson.get_tvshows + self.get_compare_info(media_type, + get_list(["originaltitle", "imdbnumber"])) + local_items = [] + remote_items = [] + info = self.info[media_type] + for item in items: + index = False + imdb_id = item.get_property("imdb_id") + title = item.get_info("title").lower() + otitle = item.get_info("originaltitle").lower() + if "imdb_id" in item.get_properties() and imdb_id in info["imdbs"]: + index = info["imdbs"].index(imdb_id) + elif "title" in item.get_infos() and title in info["titles"]: + index = info["titles"].index(title) + elif "originaltitle" in item.get_infos() and otitle in info["otitles"]: + index = info["otitles"].index(otitle) + if index: + get_info = self.get_movie if media_type == "movie" else self.get_tvshow + local_item = get_info(info["ids"][index]) + if local_item: + try: + diff = abs(int(local_item.get_info("year")) - int(item.get_info("year"))) + if diff > 1: + remote_items.append(item) + continue + except Exception: + pass + item.update_from_listitem(local_item) + if library_first: + local_items.append(item) + continue + remote_items.append(item) + if sortkey: + local_items = sorted(local_items, + key=lambda k: k.get_info(sortkey), + reverse=True) + remote_items = sorted(remote_items, + key=lambda k: k.get_info(sortkey), + reverse=True) + return ItemList(content_type=media_type + "s", + items=local_items + remote_items) + + def compare_album_with_library(self, online_list): + """ + merge *albums from online sources with local db info + """ + if not self.albums: + self.albums = self.get_albums() + for item in online_list: + for local_item in self.albums: + if not item.get_info("title") == local_item["title"]: + continue + data = kodijson.get_json(method="AudioLibrary.getAlbumDetails", + params={"properties": ["thumbnail"], "albumid": local_item["albumid"]}) + album = data["result"]["albumdetails"] + item.set_info("dbid", album["albumid"]) + item.set_path(PLUGIN_BASE + 'playalbum&&dbid=%i' % album['albumid']) + if album["thumbnail"]: + item.update_artwork({"thumb": album["thumbnail"]}) + break + return online_list + + def get_set_name(self, dbid): + """ + get name of set for movie with *dbid + """ + data = kodijson.get_json(method="VideoLibrary.GetMovieDetails", + params={"properties": ["setid"], "movieid": dbid}) + if "result" not in data or "moviedetails" not in data["result"]: + return None + set_dbid = data['result']['moviedetails'].get('setid') + if set_dbid: + data = kodijson.get_json(method="VideoLibrary.GetMovieSetDetails", + params={"setid": set_dbid}) + return data['result']['setdetails'].get('label') + + def get_artist_mbid(self, dbid): + """ + get mbid of artist with *dbid + """ + data = kodijson.get_json(method="MusicLibrary.GetArtistDetails", + params={"properties": ["musicbrainzartistid"], "artistid": dbid}) + mbid = data['result']['artistdetails'].get('musicbrainzartistid') + return mbid if mbid else None + + def get_imdb_id(self, media_type, dbid): + if not dbid: + return None + if media_type == "movie": + data = kodijson.get_json(method="VideoLibrary.GetMovieDetails", + params={"properties": ["uniqueid", "title", "year"], "movieid": int(dbid)}) + if "result" in data and "moviedetails" in data["result"]: + try: + return data['result']['moviedetails']['uniqueid']['imdb'] + except KeyError: + return None + elif media_type == "tvshow": + data = kodijson.get_json(method="VideoLibrary.GetTVShowDetails", + params={"properties": ["uniqueid", "title", "year"], "tvshowid": int(dbid)}) + if "result" in data and "tvshowdetails" in data["result"]: + try: + return data['result']['tvshowdetails']['uniqueid']['imdb'] + except KeyError: + return None + return None + + def get_tmdb_id(self, media_type, dbid): + if not dbid: + return None + if media_type == "movie": + data = kodijson.get_json(method="VideoLibrary.GetMovieDetails", + params={"properties": ["uniqueid", "title", "year"], "movieid": int(dbid)}) + if "result" in data and "moviedetails" in data["result"]: + return data['result']['moviedetails']['uniqueid'].get('tmdb', None) + elif media_type == "tvshow": + data = kodijson.get_json(method="VideoLibrary.GetTVShowDetails", + params={"properties": ["uniqueid", "title", "year"], "tvshowid": int(dbid)}) + if "result" in data and "tvshowdetails" in data["result"]: + return data['result']['tvshowdetails']['uniqueid'].get('tmdb', None) + return None + + def get_tvshow_id_by_episode(self, dbid): + if not dbid: + return None + data = kodijson.get_json(method="VideoLibrary.GetEpisodeDetails", + params={"properties": ["tvshowid"], "episodeid": dbid}) + if "episodedetails" not in data["result"]: + return None + return self.get_imdb_id(media_type="tvshow", + dbid=str(data['result']['episodedetails']['tvshowid'])) + + +def media_streamdetails(filename, streamdetails): + info = {} + video = streamdetails['video'] + audio = streamdetails['audio'] + if video: + # see StreamDetails.cpp + if video[0]['width'] + video[0]['height'] == 0: + info['VideoResolution'] = "" + elif video[0]['width'] <= 720 and video[0]['height'] <= 480: + info['VideoResolution'] = "480" + elif video[0]['width'] <= 768 and video[0]['height'] <= 576: + info['VideoResolution'] = "576" + elif video[0]['width'] <= 960 and video[0]['height'] <= 544: + info['VideoResolution'] = "540" + elif video[0]['width'] <= 1280 and video[0]['height'] <= 720: + info['VideoResolution'] = "720" + elif video[0]['width'] <= 1920 and video[0]['height'] <= 1080: + info['VideoResolution'] = "1080" + elif video[0]['width'] * video[0]['height'] >= 6000000: + info['VideoResolution'] = "4K" + else: + info['videoresolution'] = "" + info['VideoCodec'] = str(video[0]['codec']) + info["VideoAspect"] = select_aspectratio(video[0]['aspect']) + info["VideoHdrType"] = video[0].get('hdrtype', '') + if audio: + info['AudioCodec'] = audio[0]['codec'] + info['AudioChannels'] = audio[0]['channels'] + streams = [] + for i, item in enumerate(audio, start=1): + language = item['language'] + if language in streams or language == "und": + continue + streams.append(language) + streaminfo = {'AudioLanguage.%d' % i: language, + 'AudioCodec.%d' % i: item["codec"], + 'AudioChannels.%d' % i: str(item['channels'])} + info.update(streaminfo) + subs = [] + for i, item in enumerate(streamdetails['subtitle'], start=1): + language = item['language'] + if language in subs or language == "und": + continue + subs.append(language) + info.update({'SubtitleLanguage.%d' % i: language}) + return info + + +def select_aspectratio(aspect): + # see StreamDetails.cpp + aspect = float(aspect) + if aspect < 1.3499: # sqrt(1.33*1.37) + return "1.33" + elif aspect < 1.5080: # sqrt(1.37*1.66) + return "1.37" + elif aspect < 1.7190: # sqrt(1.66*1.78) + return "1.66" + elif aspect < 1.8147: # sqrt(1.78*1.85) + return "1.78" + elif aspect < 2.0174: # sqrt(1.85*2.20) + return "1.85" + elif aspect < 2.2738: # sqrt(2.20*2.35) + return "2.20" + elif aspect < 2.3749: # sqrt(2.35*2.40) + return "2.35" + elif aspect < 2.4739: # sqrt(2.40*2.55) + return "2.40" + elif aspect < 2.6529: # sqrt(2.55*2.76) + return "2.55" + else: + return "2.76" diff --git a/script.extendedinfo/resources/kutil131/player.py b/script.extendedinfo/resources/kutil131/player.py new file mode 100644 index 000000000..3a4a73c90 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/player.py @@ -0,0 +1,98 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmc +import xbmcgui + +from resources.kutil131 import busy, utils + + +class VideoPlayer(xbmc.Player): + + def __init__(self, *args, **kwargs): + super().__init__() + self.stopped = False + self.started = False + + def onPlayBackEnded(self): + self.stopped = True + self.started = False + + def onPlayBackStopped(self): + self.stopped = True + self.started = False + + def onPlayBackError(self): + self.stopped = True + self.started = False + + def onAVStarted(self): + self.started = True + self.stopped = False + + def onPlayBackStarted(self): + self.started = True + self.stopped = False + + @busy.set_busy + def youtube_info_by_id(self, youtube_id) -> None: + """function uses inop YTStreamextractor + + Args: + youtube_id (_type_): _description_ + + Returns: + _type_: function retained for future use + """ + #vid = utils.get_youtube_info(youtube_id) + vid = {} + if not vid: + return None, None + #listitem = xbmcgui.ListItem(label=vid.title) + #listitem.setArt({'thumb': vid.thumbnail}) + #listitem.setInfo(type='video', + # infoLabels={"genre": vid.sourceName, + # "plot": vid.description}) + #return vid.streamURL(), listitem + + def wait_for_video_end(self): + monitor: xbmc.Monitor = xbmc.Monitor() + while not monitor.waitForAbort(1.0): + if monitor.abortRequested(): + break + if self.stopped: + break + self.stopped = False + + def wait_for_video_start(self): + """Timer that checks if Youtube can play selected listitem + Hard coded to 15 sec + """ + monitor = xbmc.Monitor() + timeout = 15 + while not monitor.waitForAbort(1.5): #wait to see if video starts + if monitor.abortRequested(): + break + timeout += -1 + if self.started: + break + if timeout == 0: + self.stopped = True + break + + def wait_for_kodivideo_start(self): + """Timer called from dialogmovieinfo that checks if Kodi can play selected listitem + Sets a 20 sec timer to attempt play local db media. If + timer ends videoplayer self.stopped is set + """ + monitor = xbmc.Monitor() + timeout = 20 + while not monitor.waitForAbort(1): #wait to see if video starts + if monitor.abortRequested(): + break + timeout += -1 + if self.started: + break + if timeout == 0: + self.stopped = True + break diff --git a/script.extendedinfo/resources/kutil131/selectdialog.py b/script.extendedinfo/resources/kutil131/selectdialog.py new file mode 100644 index 000000000..902a24e9c --- /dev/null +++ b/script.extendedinfo/resources/kutil131/selectdialog.py @@ -0,0 +1,65 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmc +import xbmcgui + +from resources.kutil131 import addon + +C_LABEL_HEADER = 1 +C_LIST_SIMPLE = 3 +C_LIST_DETAIL = 6 +C_BUTTON_GET_MORE = 5 +C_BUTTON_CANCEL = 7 + + +class SelectDialog(xbmcgui.WindowXMLDialog): + + def __init__(self, *args, **kwargs): + xbmcgui.WindowXMLDialog.__init__(self) + self.items = kwargs.get('listing') + self.header = kwargs.get('header') + self.detailed = kwargs.get('detailed') + self.extrabutton = kwargs.get('extrabutton') + self.listitems = [i.get_listitem() for i in self.items] if self.items else [] + self.index = -1 + self.list: xbmcgui.ControlList = None + + def onInit(self): + if not self.listitems: + self.index == -1 + self.close() + self.list: xbmcgui.ControlList = self.getControl(C_LIST_DETAIL) + self.getControl(C_LIST_DETAIL).setVisible(self.detailed) + self.getControl(C_LIST_SIMPLE).setVisible(not self.detailed) + self.getControl(C_BUTTON_GET_MORE).setVisible(bool(self.extrabutton)) + if self.extrabutton: + self.getControl(C_BUTTON_GET_MORE).setLabel(self.extrabutton) + self.getControl(C_LABEL_HEADER).setLabel(self.header) + self.list.addItems(self.listitems) + self.setFocus(self.list) + + def onClick(self, control_id): + if control_id in [C_LIST_SIMPLE, C_LIST_DETAIL]: + self.index = int(self.list.getSelectedPosition()) + self.close() + elif control_id == C_BUTTON_GET_MORE: + self.index = -2 + self.close() + elif control_id == C_BUTTON_CANCEL: + self.close() + + +def open(listitems, header: str, detailed=True, extrabutton=False) -> int: + """ + open selectdialog, return index (-1 for closing, -2 for extra button) + *listitems needs to be an iterable with ListItems (array, ItemList) + """ + xbmc.executebuiltin("Dialog.Close(busydialognocancel)") + w = SelectDialog('DialogSelect.xml', addon.PATH, + listing=listitems, + header=header, + detailed=detailed, + extrabutton=extrabutton) + w.doModal() + return w.index diff --git a/script.extendedinfo/resources/kutil131/slideshow.py b/script.extendedinfo/resources/kutil131/slideshow.py new file mode 100644 index 000000000..38197874b --- /dev/null +++ b/script.extendedinfo/resources/kutil131/slideshow.py @@ -0,0 +1,41 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import os + +import xbmcgui + +from resources.kutil131 import utils + + +class SlideShow(xbmcgui.WindowXMLDialog): + ACTION_PREVIOUS_MENU = [9, 92, 10] + ID_LIST_PICTURES = 5000 + + def __init__(self, *args, **kwargs): + self.images = kwargs.get('listitems') + self.index = kwargs.get('index') + self.image = kwargs.get('image') + self.action = None + + def onInit(self): + super().onInit() + if not self.images: + return None + self.getControl(self.ID_LIST_PICTURES).addItems(utils.create_listitems(self.images)) + self.getControl(self.ID_LIST_PICTURES).selectItem(self.index) + self.setFocusId(self.ID_LIST_PICTURES) + + def onAction(self, action): + if action in self.ACTION_PREVIOUS_MENU: + self.position = self.getControl(self.ID_LIST_PICTURES).getSelectedPosition() + self.close() + + +def open(listitems, index): + slideshow = SlideShow('script-script.extendedinfo-pictureviewer.xml', + os.path.join(os.path.dirname(__file__), "..", ".."), + listitems=listitems, + index=index) + slideshow.doModal() + return slideshow.position diff --git a/script.extendedinfo/resources/kutil131/strptime_patch.py b/script.extendedinfo/resources/kutil131/strptime_patch.py new file mode 100644 index 000000000..17c6c27ce --- /dev/null +++ b/script.extendedinfo/resources/kutil131/strptime_patch.py @@ -0,0 +1,80 @@ +""" +Created on 1/11/22 + +@author: Frank Feuerbacher +""" +import datetime +import time + + +class StripTimePatch: + """ + Contains a work around for a complex bug in datetime.strptime that only + impacts embedded Python, which Kodi uses. For more info on the defect, + see: : https://bugs.python.org/issue27400 + + One option is for you to replace every reference to datetime.strptime to use + strptime_patch here. This is less voodoo, but there is always the possibility + that some library code uses strptime and there will still be potential for + incorrect results or a Kodi crash. + + The other option is to call patch_strptime at your addon's startup. This + will Monkey-Patch striptime with the version here. It is voodoo like, + but that is done a bit in Python. + """ + + @staticmethod + def monkey_patch_strptime(): + # Check if problem exists (don't want to stomp on patch applied earlier) + try: + datetime.datetime.strptime('0', '%H') + except TypeError: + # Globally replace Python's datetime.datetime.strptime with + # the version here. + + datetime.datetime = StripTimePatch.strptime + + @staticmethod + def strptime(date_string: str, date_format: str) -> datetime.datetime: + """ + Monkey-Patch dattime.strptime with time.strptime in order to + work around a known embedded python problem. + + The patch works fairly well, as long as the format does not try + to parse sub-second values. Since Python's datetime.strptime and + time.strptime sit on top of libc's strptime. + + From Python documentation on datetime.strptime + (https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior): + + classmethod datetime.strptime(date_string, format) + + Return a datetime corresponding to date_string, parsed according + to format. + + Using datetime.strptime(date_string, format) is equivalent to: + + datetime(*(time.strptime(date_string, format)[0:6])) + + except when the format includes sub-second components or timezone + offset information, which are supported in datetime.strptime but + are discarded by time.strptime. + + ValueError is raised if the date_string and format can’t be parsed + by time.strptime() or if it returns a value which isn’t a time + tuple. For a complete list of formatting directives, see + strftime() and strptime() Behavior. + + + This Python bug has been around a long time, but not fixed due to + lack of funding for embedded systems. For more info on the defect, + see: : https://bugs.python.org/issue27400 + + :param date_string: + :param date_format: + :return: + """ + result: datetime.datetime + result = datetime.datetime(*(time.strptime(date_string, + date_format)[0:6])) + return result diff --git a/script.extendedinfo/resources/kutil131/t9_search.py b/script.extendedinfo/resources/kutil131/t9_search.py new file mode 100644 index 000000000..66c2d7604 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/t9_search.py @@ -0,0 +1,230 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import ast +import os +import time +from collections import deque +from threading import Timer + +import AutoCompletion +import xbmc +import xbmcgui + +from resources.kutil131 import ActionHandler, addon, utils + +ch = ActionHandler() + +# (1st label, 2nd label) +KEYS = (("1", "ABC1"), + ("2", "DEF2"), + ("3", "GHI3"), + ("4", "JKL4"), + ("5", "MNO5"), + ("6", "PQR6"), + ("7", "STU7"), + ("8", "VWX8"), + ("9", "YZ90"), + ("DEL", "<--"), + (" ", "___"), + ("KEYB", "CLASSIC")) + + +class T9Search: + + def __init__(self, call=None, start_value="", history="Default"): + dialog = T9SearchDialog('script-script.extendedinfo-t9search.xml', + os.path.join(os.path.dirname(__file__), "..", ".."), + call=call, + start_value=start_value, + history=history) + dialog.doModal() + self.search_str = dialog.search_str + + +class T9SearchDialog(xbmcgui.WindowXMLDialog): + """ + T9 Search dialog class.dialog + params for constructor: "call", "start_value", "history" + """ + def __init__(self, *args, **kwargs): + self.callback = kwargs.get("call") + self.search_str = kwargs.get("start_value", "") + self.previous = False + self.prev_time = 0 + self.timer = None + self.color_timer = None + self._closing = False + self.setting_name = kwargs.get("history") + setting_string = addon.setting(self.setting_name) + if self.setting_name and setting_string: + self.last_searches = deque(ast.literal_eval(setting_string), maxlen=10) + else: + self.last_searches = deque(maxlen=10) + + def onInit(self): + self._closing = False + self.get_autocomplete_labels_async() + self.update_search_label_async() + listitems = [] + for i, item in enumerate(KEYS): + li = {"label": "[B]%s[/B]" % item[0], + "label2": item[1], + "key": item[0], + "value": item[1], + "index": str(i) + } + listitems.append(li) + self.getControl(9090).addItems(utils.dict_to_listitems(listitems)) + self.setFocusId(9090) + self.getControl(600).setLabel("[B]%s[/B]_" % self.search_str) + + def onClick(self, control_id): + ch.serve(control_id, self) + + def onAction(self, action): + ch.serve_action(action, self.getFocusId(), self) + + @ch.click(9090) + def panel_click(self, control_id): + listitem = self.getControl(control_id).getSelectedItem() + self.set_t9_letter(letters=listitem.getProperty("value"), + number=listitem.getProperty("key"), + button=int(listitem.getProperty("index"))) + + @ch.click(9091) + def set_autocomplete(self, control_id): + listitem = self.getControl(control_id).getSelectedItem() + self.search_str = listitem.getLabel() + self.getControl(600).setLabel("[B]%s[/B]_" % self.search_str) + self.get_autocomplete_labels_async() + if self.timer: + self.timer.cancel() + self.timer = Timer(0.0, self.search, (self.search_str,)) + self.timer.start() + + @ch.action("parentdir", "*") + @ch.action("parentfolder", "*") + @ch.action("previousmenu", "*") + def close_dialog(self, control_id): + """ + save autocompletion and close dialog + """ + self._closing = True + self.save_autocomplete() + self.close() + + @ch.action("number0", "*") + def set_0(self, control_id): + """ + deal with 0 action (either via gui or via remotekeys) + """ + listitem = self.getControl(control_id).getListItem(10) + self.set_t9_letter(letters=listitem.getProperty("value"), + number=listitem.getProperty("key"), + button=int(listitem.getProperty("index"))) + + @ch.action("number1", "*") + @ch.action("number2", "*") + @ch.action("number3", "*") + @ch.action("number4", "*") + @ch.action("number5", "*") + @ch.action("number6", "*") + @ch.action("number7", "*") + @ch.action("number8", "*") + @ch.action("number9", "*") + def t_9_button_click(self, control_id): + """ + deal with number actions (either via gui or via remotekeys) + """ + item_id = self.action_id - xbmcgui.REMOTE_1 + listitem = self.getControl(control_id).getListItem(item_id) + self.set_t9_letter(letters=listitem.getProperty("value"), + number=listitem.getProperty("key"), + button=int(listitem.getProperty("index"))) + + @utils.run_async + def update_search_label_async(self): + + monitor: xbmc.Monitor = xbmc.Monitor() + while not monitor.waitForAbort(1.0): + if self._closing: + break + + # TODO: Blink every second (probably a better way to do this, like animation) + if int(time.time()) % 2 == 0: + self.getControl(600).setLabel("[B]%s[/B]_" % self.search_str) + else: + self.getControl(600).setLabel("[B]%s[/B][COLOR 00FFFFFF]_[/COLOR]" % self.search_str) + + @utils.run_async + def get_autocomplete_labels_async(self): + self.getControl(9091).reset() + if self.search_str: + listitems = AutoCompletion.get_autocomplete_items(self.search_str) + else: + listitems = list(self.last_searches) + self.getControl(9091).addItems(utils.dict_to_listitems(listitems)) + + def save_autocomplete(self): + """ + save last searches + """ + if not self.search_str: + return None + listitem = {"label": self.search_str} + if listitem in self.last_searches: + self.last_searches.remove(listitem) + self.last_searches.appendleft(listitem) + addon.set_setting(self.setting_name, str(list(self.last_searches))) + + def set_t9_letter(self, letters, number, button): + now = time.time() + time_diff = now - self.prev_time + if number == "DEL": + self.search_str = self.search_str[:-1] + elif number == " ": + if self.search_str: + self.search_str += " " + elif number == "KEYB": + self.use_classic_search() + elif self.previous != letters or time_diff >= 1: + self.prev_time = now + self.previous = letters + self.search_str += letters[0] + self.color_labels(0, letters, button) + elif time_diff < 1: + if self.color_timer: + self.color_timer.cancel() + self.prev_time = now + idx = (letters.index(self.search_str[-1]) + 1) % len(letters) + self.search_str = self.search_str[:-1] + letters[idx] + self.color_labels(idx, letters, button) + if self.timer: + self.timer.cancel() + self.timer = Timer(1.0, self.search, (self.search_str,)) + self.timer.start() + self.getControl(600).setLabel("[B]%s[/B]_" % self.search_str) + self.get_autocomplete_labels_async() + + def use_classic_search(self): + """ + open classic keyboard dialog and call callback when result is valid + """ + self.close() + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if result and result > -1: + self.search_str = result + self.callback(self.search_str) + self.save_autocomplete() + + def search(self, search_str): + self.callback(search_str) + + def color_labels(self, index, letters, button): + letter = letters[index] + label = "[COLOR=FFFF3333]%s[/COLOR]" % letter + self.getControl(9090).getListItem(button).setLabel2(letters.replace(letter, label)) + self.color_timer = Timer(1.0, utils.reset_color, (self.getControl(9090).getListItem(button),)) + self.color_timer.start() diff --git a/script.extendedinfo/resources/kutil131/utils.py b/script.extendedinfo/resources/kutil131/utils.py new file mode 100644 index 000000000..a45541d9e --- /dev/null +++ b/script.extendedinfo/resources/kutil131/utils.py @@ -0,0 +1,638 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details +import datetime +import hashlib +import json +import os +import re +import sys +import threading +import time +import traceback +import urllib.error +import urllib.parse +import urllib.request +from functools import wraps +from io import StringIO + +import requests +import xbmc +import xbmcgui +import xbmcvfs + +#import YDStreamExtractor +from resources.kutil131 import addon + + +def youtube_info_by_id(youtube_id) -> tuple: + """Gets youtube video info from YDSStreamExtractor + Currently inop due to YDSStreamextractor not maintained. + + Args: + youtube_id (_type_): _description_ + + Returns: + _type_: _description_ + """ + #vid = get_youtube_info(youtube_id) + vid = None #added + if not vid: + return None, None + url = vid.streamURL() + listitem = xbmcgui.ListItem(label=vid.title, path=url) + listitem.setArt({'thumb': vid.thumbnail}) + listitem.setInfo(type='video', + infoLabels={"genre": vid.sourceName, + "path": url, + "plot": vid.description}) + listitem.setProperty("isPlayable", "true") + return url, listitem + + +#def get_youtube_info(youtube_id): +# return YDStreamExtractor.getVideoInfo(youtube_id, +# quality=1) + + +def log(*args): + for arg in args: + message = '%s: %s' % (addon.ID, arg) + xbmc.log(msg=message, + level=xbmc.LOGDEBUG) + + +def dump_all_threads(delay: float = None) -> None: + """ + Dumps all Python stacks, including those in other plugins + + :param delay: + :return: + """ + if delay is None or delay == 0: + _dump_all_threads() + else: + dump_threads = threading.Timer(delay, _dump_all_threads) + dump_threads.setName('dump_threads') + dump_threads.start() + + +def _dump_all_threads() -> None: + """ + Worker method that dumps all threads. + + :return: + """ + addon_prefix = f'{addon.ID}/' + xbmc.log('dump_all_threads', xbmc.LOGDEBUG) + sio = StringIO() + sio.write('\n*** STACKTRACE - START ***\n\n') + code = [] + # Monitor.dump_wait_counts() + # for threadId, stack in sys._current_frames().items(): + for th in threading.enumerate(): + sio.write(f'\n# ThreadID: {th.name} Daemon: {th.isDaemon()}\n\n') + stack = sys._current_frames().get(th.ident, None) + if stack is not None: + traceback.print_stack(stack, file=sio) + + string_buffer: str = sio.getvalue() + '\n*** STACKTRACE - END ***\n' + sio.close() + msg = addon.ID + ' : dump_all_threads' + xbmc.log(msg, xbmc.LOGDEBUG) + xbmc.log(string_buffer, xbmc.LOGDEBUG) + + ''' + try: + dump_path = Constants.FRONTEND_DATA_PATH + '/stack_dump' + + dump_file = io.open(dump_path, mode='at', buffering=1, newline=None, + encoding='ascii') + + faulthandler.dump_traceback(file=dump_file, all_threads=True) + except Exception as e: + pass + ''' + + +def format_seconds(seconds): + if not seconds: + return None + hours, remainder = divmod(seconds, 3600) + minutes, seconds = divmod(remainder, 60) + return '%02d:%02d:%02d' % (hours, minutes, seconds) + + +def dump_dict(dct): + return json.dumps(dct, + ensure_ascii=False, + sort_keys=True, + indent=4, + separators=(',', ': ')) + + +def pp(string): + """ + prettyprint json + """ + log(dump_dict(string)) + + +def dictfind(lst, key, value): + """ + searches through a list of dicts, returns dict where dict[key] = value + """ + for i, dic in enumerate(lst): + if dic[key] == value: + return dic + return "" + + +def merge_dicts(*dict_args): + ''' + Given any number of dicts, shallow copy and merge into a new dict, + precedence goes to key value pairs in latter dicts. + ''' + result = {} + for dictionary in dict_args: + result.update(dictionary) + return result + + +def check_version(): + """ + check version, open TextViewer if update detected + """ + pass + # if not addon.setting("changelog_version") == addon.VERSION: + # xbmcgui.Dialog().textviewer(heading=addon.LANG(24036), + # text=read_from_file(addon.CHANGELOG, True)) + # addon.set_setting("changelog_version", addon.VERSION) + + +def get_skin_string(name): + """ + get String with name *name + """ + return xbmc.getInfoLabel(f"Skin.String({name})") + + +def set_skin_string(name, value): + """ + Set String *name to value *value + """ + xbmc.executebuiltin(f"Skin.SetString({name}, {value})") + + +def run_async(func): + """ + Decorator to run a function in a separate thread + """ + @wraps(func) + def async_func(*args, **kwargs): + func_hl = threading.Thread(target=func, + args=args, + kwargs=kwargs) + func_hl.start() + return func_hl + + return async_func + + +def contextmenu(options): + """ + pass list of tuples (index, label), get index + """ + index = xbmcgui.Dialog().contextmenu(list=[i[1] for i in options]) + if index > -1: + return [i[0] for i in options][index] + + +def extract_youtube_id(raw_string): + """ + get youtube video id if from youtube URL + """ + vid_ids = None + if raw_string and 'youtube.com/v' in raw_string: + vid_ids = re.findall('http://www.youtube.com/v/(.{11})\??', raw_string, re.DOTALL) + elif raw_string and 'youtube.com/watch' in raw_string: + vid_ids = re.findall('youtube.com/watch\?v=(.{11})\??', raw_string, re.DOTALL) + if vid_ids: + return vid_ids[0] + else: + return "" + + +# def download_video(youtube_id): +# """ +# download youtube video with id *youtube_id +# """ +# vid = YDStreamExtractor.getVideoInfo(youtube_id, +# quality=1) +# YDStreamExtractor.handleDownload(vid) + + +def notify(header="", message="", icon=addon.ICON, ntime=5000, sound=True): + """ + show kodi notification dialog + """ + xbmcgui.Dialog().notification(heading=header, + message=message, + icon=icon, + time=ntime, + sound=sound) + + +def millify(n): + """ + make large numbers human-readable, return string + """ + millnames = [' ', ',000', ' ' + addon.LANG(32000), ' ' + addon.LANG(32001), ' ' + addon.LANG(32002)] + if not n or n <= 100: + return "" + n = float(n) + char_count = len(str(n)) + millidx = int(char_count / 3) - 1 + if millidx == 3 or char_count == 9: + return '%.2f%s' % (n / 10 ** (3 * millidx), millnames[millidx]) + else: + return '%.0f%s' % (n / 10 ** (3 * millidx), millnames[millidx]) + + +def get_year(year_string): + """ + return last 4 chars of string + """ + return year_string[:4] if year_string else "" + + +def format_time(ftime:int, time_format=None): + """ + get formatted time + time (int): duration in secs + time_format = h, m or None + """ + try: + intTime = int(ftime) + except Exception: + return ftime + #hour = str(intTime / 60) + #minute = str(intTime % 60).zfill(2) + minute, second = divmod(ftime, 60) + hour, minute = divmod(minute, 60) + if time_format == "h": + return str(hour) + elif time_format == "m": + return str(minute) + elif time_format == 's': + return str(second) + elif intTime >= 3600: + return hour + " h " + minute + " min" + else: + return minute + " min" + + +def input_userrating(preselect=-1): + """ + opens selectdialog and returns chosen userrating. + """ + index = xbmcgui.Dialog().select(heading=addon.LANG(38023), + list=[addon.LANG(10035)] + [str(i) for i in range(1, 11)], + preselect=preselect) + if index == preselect: + return -1 + return index + + +def save_to_file(content, filename, path): + """ + dump json and save to *filename in *path + """ + if not xbmcvfs.exists(path): + xbmcvfs.mkdirs(path) + text_file_path = os.path.join(path, filename + ".txt") + text_file = xbmcvfs.File(text_file_path, "w") + json.dump(content, text_file) + text_file.close() + return True + + +def read_from_file(path, raw=False): + """ + return data from file with *path + """ + if not xbmcvfs.exists(path): + return False + try: + with open(path) as f: + # utils.log("opened textfile %s." % (path)) + if not raw: + result = json.load(f) + else: + result = f.read() + return result + except Exception: + log("failed to load textfile: " + path) + return False + + +def create_listitems(data=None, preload_images=0): + """ + returns list with xbmcgui listitems + """ + return [item.get_listitem() for item in data] if data else [] + + +def translate_path(arg1, *args): + return xbmcvfs.translatePath(os.path.join(arg1, *args)) + + +def get_infolabel(name): + """ + returns infolabel with *name + """ + return xbmc.getInfoLabel(name) + + +def calculate_age(born, died=False): + """ + calculate age based on born / died + display notification for birthday + return death age when already dead + """ + if died: + ref_day = died.split("-") + elif born: + date = datetime.date.today() + ref_day = [date.year, date.month, date.day] + else: + return "" + actor_born = born.split("-") + try: + base_age = int(ref_day[0]) - int(actor_born[0]) + except ValueError as err: + log(f'utils.calculate_age fail for actor_born {actor_born} with error {err}') + return "" + if len(actor_born) > 1: + diff_months = int(ref_day[1]) - int(actor_born[1]) + diff_days = int(ref_day[2]) - int(actor_born[2]) + if diff_months < 0 or (diff_months == 0 and diff_days < 0): + base_age -= 1 + elif diff_months == 0 and diff_days == 0 and not died: + notify(f"{addon.LANG(32158)} ({base_age})") + return base_age + + +def get_http(url, headers=False): + """ + fetches data from *url as http GET, returns it as a string + """ + succeed = 0 + if not headers: + headers = {'User-agent': 'Kodi/19.0 ( fbacher@kodi.tv )'} + while (succeed < 2) and (not xbmc.Monitor().abortRequested()): + try: + request = requests.get(url, headers=headers, timeout=10) + return request.text + except requests.exceptions.RequestException as err: + log(f"get_http: could not get data from {url} exception {err}") + xbmc.sleep(1000) + succeed += 1 + return None + + +def post(url, values, headers): + """ + retuns answer to post request + """ + try: + request = requests.post(url=url, + data=json.dumps(values), + headers=headers, + timeout=10) + except requests.exceptions.RequestException as err: + log(f"get_http: could not get data from {url} exception {err}") + return json.loads(request.text) + + +def delete(url, values, headers): + """ + returns answer to delete request + """ + try: + request = requests.delete(url=url, + data=json.dumps(values), + headers=headers, + timeout=10) + except requests.exceptions.RequestException as err: + log(f"get_http: could not get data from {url} exception {err}") + return json.loads(request.text) + + +def get_JSON_response(url="", cache_days=7.0, folder=False, headers=False) -> dict: + """gets JSON response for *url, makes use of prop and file cache. + + Args: + url (str, optional): search query URL. Defaults to "". + cache_days (float, optional): Number of days to determine cache is stale. Defaults to 7.0. + folder (bool, optional): folder on local system to cache query results. Defaults to False. + headers (bool, optional): headers to use in https request. Defaults to False. + + Returns: + dict: a deserialized JSON query response or None + """ + now = time.time() + hashed_url = hashlib.md5(url.encode("utf-8", "ignore")).hexdigest() + cache_path = translate_path(addon.DATA_PATH, folder) if folder else translate_path(addon.DATA_PATH) + cache_seconds = int(cache_days * 86400.0) + if not cache_days: + addon.clear_global(hashed_url) + addon.clear_global(hashed_url + "_timestamp") + prop_time = addon.get_global(hashed_url + "_timestamp") + if prop_time and now - float(prop_time) < cache_seconds: + try: + prop = json.loads(addon.get_global(hashed_url)) + if prop: + return prop + except Exception: + pass + path = os.path.join(cache_path, hashed_url + ".txt") + if xbmcvfs.exists(path) and ((now - os.path.getmtime(path)) < cache_seconds): + results = read_from_file(path) + else: + response = get_http(url, headers) + try: + results = json.loads(response) + # utils.log("download %s. time: %f" % (url, time.time() - now)) + if "status_code" in results and results.get("status_code") == 1: + save_to_file(results, hashed_url, cache_path) + except Exception as err: + log(f"Exception: Could not get new JSON data from {url} " + f"with error {err}. Trying to fallback to cache") + #log(f'kutils131.utils.get_JSON_response {response}') + results = read_from_file(path) if xbmcvfs.exists(path) else [] + if not results: + return None + addon.set_global(hashed_url + "_timestamp", str(now)) + addon.set_global(hashed_url, json.dumps(results)) + return results + + +def dict_to_windowprops(data:dict=None, prefix="", window_id=10000): + """Sets window property keys / values from dict + + Args: + data (dict optional): the data to be set as properties Defaults to None. + prefix (str, optional): a prefix for the property key Defaults to "". + window_id (int, optional): Kodi window id. Defaults to 10000. + """ + window = xbmcgui.Window(window_id) + if not data: + return None + for (key, value) in data.items(): + value = str(value) + window.setProperty(f'{prefix}{key}', value) + + +def get_file(url): + clean_url = translate_path(urllib.parse.unquote(url)).replace("image://", "") + clean_url = clean_url.rstrip("/") + cached_thumb = xbmc.getCacheThumbName(clean_url) + vid_cache_file = os.path.join("special://profile/Thumbnails/Video", + cached_thumb[0], + cached_thumb) + cache_file_jpg = os.path.join("special://profile/Thumbnails/", + cached_thumb[0], + cached_thumb[:-4] + ".jpg").replace("\\", "/") + cache_file_png = cache_file_jpg[:-4] + ".png" + if xbmcvfs.exists(cache_file_jpg): + log("cache_file_jpg Image: " + url + "-->" + cache_file_jpg) + return translate_path(cache_file_jpg) + elif xbmcvfs.exists(cache_file_png): + log("cache_file_png Image: " + url + "-->" + cache_file_png) + return cache_file_png + elif xbmcvfs.exists(vid_cache_file): + log("vid_cache_file Image: " + url + "-->" + vid_cache_file) + return vid_cache_file + try: + request = urllib.request.Request(clean_url) + request.add_header('Accept-encoding', 'gzip') + response = urllib.request.urlopen(request, timeout=3) + data = response.read() + response.close() + log(f'image downloaded: {clean_url}') + except Exception: + log(f'image download failed: {clean_url}') + return "" + if not data: + return "" + image = cache_file_png if url.endswith(".png") else cache_file_jpg + try: + with open(translate_path(image), "wb") as f: + f.write(data) + return translate_path(image) + except Exception: + log(f'failed to save image {url}') + return "" + + +def fetch_musicbrainz_id(artist, artist_id=-1): + """ + fetches MusicBrainz ID for given *artist and returns it + uses musicbrainz.org + """ + base_url = "http://musicbrainz.org/ws/2/artist/?fmt=json" + url = f'&query=artist:{urllib.parse.quote_plus(artist.encode("utf-8"))}' + results = get_JSON_response(url=base_url + url, + cache_days=30, + folder="MusicBrainz") + if results and len(results["artists"]) > 0: + log(f'found artist id for {artist}: {results["artists"][0]["id"]}') + return results["artists"][0]["id"] + else: + return None + + +class FunctionThread(threading.Thread): + + def __init__(self, function=None, param=None): + super().__init__() + self.function = function + self.param = param + self.setName(self.function.__name__) + log("init " + self.function.__name__) + + def run(self): + self.listitems = self.function(self.param) + return True + + +def reset_color(item): + label = item.getLabel2() + label = label.replace("[COLOR=FFFF3333]", "").replace("[/COLOR]", "") + item.setLabel2(label) + + +def dict_to_listitems(data=None): + if not data: + return [] + itemlist = [] + for (count, result) in enumerate(data): + listitem = xbmcgui.ListItem(f'{str(count)}') + for (key, value) in result.items(): + if not value: + continue + value = str(value) + if key.lower() in ["name", "label"]: + listitem.setLabel(value) + elif key.lower() in ["label2"]: + listitem.setLabel2(value) + elif key.lower() in ["path"]: + listitem.setPath(path=value) + listitem.setProperty('%s' % (key), value) + listitem.setProperty("index", str(count)) + itemlist.append(listitem) + return itemlist + + +def pretty_date(btime=False): + """ + Get a datetime object or a int() Epoch timestamp and return a + pretty string like 'an hour ago', 'Yesterday', '3 months ago', + 'just now', etc + # https://stackoverflow.com/questions/1551382/user-friendly-time-format-in-python + """ + now = datetime.datetime.now() + if isinstance(btime, int): + diff = now - datetime.datetime.fromtimestamp(btime) + elif isinstance(btime, datetime.datetime): + diff = now - btime + elif not btime: + diff = now - now + second_diff = diff.seconds + day_diff = diff.days + + if day_diff < 0: + return '' + + if day_diff == 0: + if second_diff < 10: + return "just now" + if second_diff < 60: + return str(second_diff) + " seconds ago" + if second_diff < 120: + return "a minute ago" + if second_diff < 3600: + return str(second_diff / 60) + " minutes ago" + if second_diff < 7200: + return "an hour ago" + if second_diff < 86400: + return str(second_diff / 3600) + " hours ago" + if day_diff == 1: + return "Yesterday" + if day_diff < 7: + return str(day_diff) + " days ago" + if day_diff < 31: + return str(day_diff / 7) + " weeks ago" + if day_diff < 365: + return str(day_diff / 30) + " months ago" + return str(day_diff / 365) + " years ago" diff --git a/script.extendedinfo/resources/kutil131/windows.py b/script.extendedinfo/resources/kutil131/windows.py new file mode 100644 index 000000000..425a4d3c2 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/windows.py @@ -0,0 +1,59 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +import xbmcgui + + +class WindowMixin: + + def __init__(self, *args, **kwargs): + super().__init__() + self.cancelled = False + + def FocusedItem(self, control_id): + try: + control = self.getControl(control_id) + listitem = control.getSelectedItem() + if not listitem: + listitem = self.getListItem(self.getCurrentListPosition()) + return listitem + except Exception as err: + return None + + def set_visible(self, control_id, condition): + try: + self.getControl(control_id).setVisible(bool(condition)) + return True + except Exception: + return False + + def check_visible(self, control_id): + try: + self.getControl(control_id) + return True + except Exception: + return False + + def exit(self): + self.cancelled = True + self.close() + + +class WindowXML(xbmcgui.WindowXML, WindowMixin): + + def __init__(self, *args, **kwargs): + super().__init__() + self.window_type = "window" + + def onInit(self): + self.window_id = xbmcgui.getCurrentWindowId() + + +class DialogXML(xbmcgui.WindowXMLDialog, WindowMixin): + + def __init__(self, *args, **kwargs): + super().__init__() + self.window_type = "dialog" + + def onInit(self): + self.window_id = xbmcgui.getCurrentWindowDialogId() diff --git a/script.extendedinfo/resources/kutil131/youtube.py b/script.extendedinfo/resources/kutil131/youtube.py new file mode 100644 index 000000000..52a813e30 --- /dev/null +++ b/script.extendedinfo/resources/kutil131/youtube.py @@ -0,0 +1,264 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# This program is Free Software see LICENSE file for details + +from __future__ import annotations +import html +import itertools +import urllib.error +import urllib.parse +import urllib.request + +from resources.kutil131 import ItemList, VideoItem, utils + +BASE_URL = "https://www.googleapis.com/youtube/v3/" +PLUGIN_BASE = "plugin://script.extendedinfo/?info=" + + +def handle_videos(results:list[dict], extended=False, api_key=''): + """ + Process video api result to ItemList + + :param api_key: api_key to pass to YouTube + """ + videos = ItemList(content_type="videos") + for item in results: + snippet = item["snippet"] + thumb = snippet["thumbnails"]["high"]["url"] if "thumbnails" in snippet else "" + try: + video_id = item["id"]["videoId"] + except Exception: + video_id = snippet["resourceId"]["videoId"] + video = VideoItem(label=html.unescape(snippet["title"]), + path=PLUGIN_BASE + 'youtubevideo&&id=%s' % video_id) + video.set_infos({'plot': html.unescape(snippet["description"]), + 'mediatype': "video", + 'premiered': snippet["publishedAt"][:10]}) + video.set_artwork({'thumb': thumb}) + video.set_playable(True) + video.set_properties({'channel_title': html.unescape(snippet["channelTitle"]), + 'channel_id': snippet["channelId"], + 'type': "video", + 'youtube_id': video_id}) + videos.append(video) + if not extended: + return videos + params = {"part": "contentDetails,statistics", + "id": ",".join([i.get_property("youtube_id") for i in videos]), + "key": api_key} + ext_results = get_data(method="videos", + params=params) + if not ext_results or not 'items' in ext_results.keys(): + return videos + for item in videos: + for ext_item in ext_results["items"]: + if not item.get_property("youtube_id") == ext_item['id']: + continue + details = ext_item['contentDetails'] + stats = ext_item['statistics'] + likes = stats.get('likeCount') + dislikes = stats.get('dislikeCount') + item.update_infos({"duration": get_duration_in_seconds(details['duration'])}) + props = {"duration": details['duration'][2:].lower(), + "formatted_duration": get_formatted_duration(details['duration']), + "dimension": details['dimension'], + "definition": details['definition'], + "caption": details['caption'], + "viewcount": utils.millify(int(stats.get('viewCount', 0))), + "likes": likes, + "dislikes": dislikes} + item.update_properties(props) + if likes and dislikes: + vote_count = int(likes) + int(dislikes) + if vote_count > 0: + item.set_info("rating", round(float(likes) / vote_count * 10, 1)) + break + return videos + + +def get_duration_in_seconds(duration:str) -> int: + """ + convert youtube duration string to seconds int + """ + if not duration.endswith('S'): + duration = duration + '0S' + try: + duration = duration[2:-1].replace("H", "M").split("M") + if len(duration) == 3: + return int(duration[0]) * 3600 + int(duration[1]) * 60 + int(duration[2]) + elif len(duration) == 2: + return int(duration[0]) * 60 + int(duration[1]) + else: + return int(duration[0]) + except Exception as err: + utils.log(f'kutils131.youtube unable decode youtube duration of {duration} error {err}') + return 0 + + +def get_formatted_duration(duration): + """ + convert youtube duration string to formatted duration + """ + duration = duration[2:-1].replace("H", "M").split("M") + if len(duration) == 3: + return "{}:{}:{}".format(duration[0].zfill(2), duration[1].zfill(2), duration[2].zfill(2)) + elif len(duration) == 2: + return "{}:{}".format(duration[0].zfill(2), duration[1].zfill(2)) + else: + return "00:{}".format(duration[0].zfill(2)) + + +def handle_playlists(results, api_key=''): + """ + process playlist api result to ItemList + + :param api_key: api_key to pass to YouTube + + """ + playlists = ItemList(content_type="videos") + for item in results: + snippet = item["snippet"] + thumb = snippet["thumbnails"]["high"]["url"] if "thumbnails" in snippet else "" + try: + playlist_id = item["id"]["playlistId"] + except Exception: + playlist_id = snippet["resourceId"]["playlistId"] + playlist = VideoItem(label=snippet["title"], + path=PLUGIN_BASE + 'youtubeplaylist&&id=%s' % playlist_id) + playlist.set_infos({'plot': snippet["description"], + "mediatype": "video", + 'premiered': snippet["publishedAt"][:10]}) + playlist.set_art("thumb", thumb) + playlist.set_properties({'youtube_id': playlist_id, + 'channel_title': snippet["channelTitle"], + 'type': "playlist", + 'live': snippet["liveBroadcastContent"].replace("none", "")}) + playlists.append(playlist) + params = {"id": ",".join([i.get_property("youtube_id") for i in playlists]), + "part": "contentDetails", + "key": api_key} + ext_results = get_data(method="playlists", + params=params) + for item, ext_item in itertools.product(playlists, ext_results["items"]): + if item.get_property("youtube_id") == ext_item['id']: + item.set_property("itemcount", ext_item['contentDetails']['itemCount']) + return playlists + + +def handle_channels(results, api_key=''): + """ + process channel api result to ItemList + + :param api_key: api_key to pass to YouTube + + """ + channels = ItemList(content_type="videos") + for item in results: + snippet = item["snippet"] + thumb = snippet["thumbnails"]["high"]["url"] if "thumbnails" in snippet else "" + try: + channel_id = item["id"]["channelId"] + except Exception: + channel_id = snippet["resourceId"]["channelId"] + channel = VideoItem(label=html.unescape(snippet["title"]), + path=PLUGIN_BASE + 'youtubechannel&&id=%s' % channel_id) + channel.set_infos({'plot': html.unescape(snippet["description"]), + 'mediatype': "video", + 'premiered': snippet["publishedAt"][:10]}) + channel.set_art("thumb", thumb) + channel.set_properties({"youtube_id": channel_id, + "type": "channel"}) + channels.append(channel) + channel_ids = [item.get_property("youtube_id") for item in channels] + params = {"id": ",".join(channel_ids), + "part": "contentDetails,statistics,brandingSettings", + "key": api_key} + ext_results = get_data(method="channels", + params=params) + for item, ext_item in itertools.product(channels, ext_results["items"]): + if item.get_property("youtube_id") == ext_item['id']: + item.set_property("itemcount", ext_item['statistics']['videoCount']) + item.set_art("fanart", ext_item["brandingSettings"]["image"].get("bannerTvMediumImageUrl")) + return channels + + +def get_data(method, params=None, cache_days=0.5): + """ + fetch data from youtube API + """ + params = params if params else {} +# params["key"] = YT_KEY + params = {k: str(v) for k, v in iter(params.items()) if v} + url = "{base_url}{method}?{params}".format(base_url=BASE_URL, + method=method, + params=urllib.parse.urlencode(params)) + return utils.get_JSON_response(url=url, + cache_days=cache_days, + folder="YouTube") + + +def search(search_str="", hd="", orderby="relevance", limit=40, extended=True, + page="", filters=None, media_type="video", api_key="") -> ItemList: + """ + returns ItemList according to search term, filters etc. + + :param api_key: api_key to pass to YouTube + """ + params = {"part": "id,snippet", + "maxResults": limit, + "type": media_type, + "order": orderby, + "pageToken": page, + "hd": str(hd and not hd == "false"), + "q": search_str.replace('"', ''), + "key" : api_key} + results = get_data(method="search", + params=utils.merge_dicts(params, filters if filters else {})) + if 'error' in results.keys(): + utils.log('youtube get_data ERROR: {error}'.format(error=results.get('error').get('message'))) + if not results or 'items' not in results.keys(): + return None + + # Give initial value to keep IDE happy as well as in case we drop through all + # choices + + listitems: ItemList = ItemList() + if media_type == "video": + listitems = handle_videos(results["items"], extended=extended, api_key=api_key) + elif media_type == "playlist": + listitems = handle_playlists(results["items"], api_key=api_key) + elif media_type == "channel": + listitems = handle_channels(results["items"], api_key=api_key) + listitems.total_pages = results["pageInfo"]["resultsPerPage"] + listitems.totals = results["pageInfo"]["totalResults"] + listitems.next_page_token = results.get("nextPageToken", "") + listitems.prev_page_token = results.get("prevPageToken", "") + return listitems + + +def get_playlist_videos(playlist_id=""): + """ + returns ItemList from playlist with *playlist_id + """ + if not playlist_id: + return [] + params = {"part": "id,snippet", + "maxResults": "50", + "playlistId": playlist_id} + results = get_data(method="playlistItems", + params=params) + if not results: + return [] + return handle_videos(results["items"]) + + +def get_user_playlists(username=""): + """ + returns ItemList with user uploads from *username + """ + params = {"part": "contentDetails", + "forUsername": username} + results = get_data(method="channels", + params=params) + if not results["items"]: + return None + return results["items"][0]["contentDetails"]["relatedPlaylists"] diff --git a/script.extendedinfo/resources/language/resource.language.bg_bg/strings.po b/script.extendedinfo/resources/language/resource.language.bg_bg/strings.po index 6d50482de..2dc91cb72 100644 --- a/script.extendedinfo/resources/language/resource.language.bg_bg/strings.po +++ b/script.extendedinfo/resources/language/resource.language.bg_bg/strings.po @@ -724,3 +724,7 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" diff --git a/script.extendedinfo/resources/language/resource.language.cs_cz/strings.po b/script.extendedinfo/resources/language/resource.language.cs_cz/strings.po index 84dbcc1ac..30140f3b9 100644 --- a/script.extendedinfo/resources/language/resource.language.cs_cz/strings.po +++ b/script.extendedinfo/resources/language/resource.language.cs_cz/strings.po @@ -775,3 +775,7 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" diff --git a/script.extendedinfo/resources/language/resource.language.en_gb/strings.po b/script.extendedinfo/resources/language/resource.language.en_gb/strings.po index 5747ed245..0abe25cb5 100644 --- a/script.extendedinfo/resources/language/resource.language.en_gb/strings.po +++ b/script.extendedinfo/resources/language/resource.language.en_gb/strings.po @@ -772,3 +772,7 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" diff --git a/script.extendedinfo/resources/language/resource.language.es_es/strings.po b/script.extendedinfo/resources/language/resource.language.es_es/strings.po index 6570694c0..eb2b1c1c1 100644 --- a/script.extendedinfo/resources/language/resource.language.es_es/strings.po +++ b/script.extendedinfo/resources/language/resource.language.es_es/strings.po @@ -772,3 +772,7 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" diff --git a/script.extendedinfo/resources/language/resource.language.fr_fr/strings.po b/script.extendedinfo/resources/language/resource.language.fr_fr/strings.po index 99fb4fe57..5e0699ddd 100644 --- a/script.extendedinfo/resources/language/resource.language.fr_fr/strings.po +++ b/script.extendedinfo/resources/language/resource.language.fr_fr/strings.po @@ -772,3 +772,8 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" + diff --git a/script.extendedinfo/resources/language/resource.language.he_il/strings.po b/script.extendedinfo/resources/language/resource.language.he_il/strings.po index 7e9dfc46e..28294a3b1 100644 --- a/script.extendedinfo/resources/language/resource.language.he_il/strings.po +++ b/script.extendedinfo/resources/language/resource.language.he_il/strings.po @@ -776,3 +776,8 @@ msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" + diff --git a/script.extendedinfo/resources/language/resource.language.it_it/strings.po b/script.extendedinfo/resources/language/resource.language.it_it/strings.po index cd545c889..829e9b630 100644 --- a/script.extendedinfo/resources/language/resource.language.it_it/strings.po +++ b/script.extendedinfo/resources/language/resource.language.it_it/strings.po @@ -772,3 +772,7 @@ msgstr "" msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" diff --git a/script.extendedinfo/resources/language/resource.language.pl_pl/strings.po b/script.extendedinfo/resources/language/resource.language.pl_pl/strings.po index 708d2aedb..e66614763 100644 --- a/script.extendedinfo/resources/language/resource.language.pl_pl/strings.po +++ b/script.extendedinfo/resources/language/resource.language.pl_pl/strings.po @@ -776,3 +776,8 @@ msgctxt "#32174" msgid "OMDb API Key" msgstr "" + +msgctxt "#32184" +msgid "ExtendedInfo Script" +msgstr "" + diff --git a/script.extendedinfo/resources/lib/BandsInTown.py b/script.extendedinfo/resources/lib/BandsInTown.py deleted file mode 100644 index 869dfe7ac..000000000 --- a/script.extendedinfo/resources/lib/BandsInTown.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details -"""Obtains local events from BandsInTown - -The current API TOU does not permit getting an API key for this addon. This -module is inop, but retained in case there is a change in the future. - -""" - -import urllib.error -import urllib.parse -import urllib.request - -from kutils import ItemList, VideoItem, utils - -# TVRAGE_KEY = 'VBp9BuIr5iOiBeWCFRMG' -API_KEY = '' -BASE_URL = "http://api.bandsintown.com/events/search?format=json&api_version=2.0&app_id=%s&" % API_KEY - - -def handle_events(results): - events = ItemList() - for event in results: - venue = event['venue'] - item = VideoItem(label=venue['name']) - item.set_properties({'date': event['datetime'].replace("T", " - ").replace(":00", "", 1), - 'city': venue['city'], - 'lat': venue['latitude'], - 'lon': venue['longitude'], - 'id': venue['id'], - 'url': venue['url'], - 'region': venue['region'], - 'country': venue['country'], - 'artists': " / ".join([art for art in event["artists"]])}) - events.append(item) - return events - - -def get_near_events(artists): # not possible with api 2.0 - arts = [urllib.parse.quote(art['artist'].encode("utf-8")) - for art in artists[:50]] - artist_str = 'artists[]=' + '&artists[]='.join(arts) - url = BASE_URL + \ - 'location=use_geoip&radius=50&per_page=100&%s' % (artist_str) - results = utils.get_JSON_response(url, folder="BandsInTown") - if results: - return handle_events(results) - return [] diff --git a/script.extendedinfo/resources/lib/LastFM.py b/script.extendedinfo/resources/lib/LastFM.py deleted file mode 100644 index 8ee346cfa..000000000 --- a/script.extendedinfo/resources/lib/LastFM.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details -"""Uses LastFM API to query data from LastFM. - -The get_* functions are called to query LastFM API. - -""" - -import re -from typing import Optional -import urllib.error -import urllib.parse -import urllib.request - -from kutils import ItemList, utils - -LAST_FM_API_KEY = 'd942dd5ca4c9ee5bd821df58cf8130d4' -GOOGLE_MAPS_KEY = 'AIzaSyBESfDvQgWtWLkNiOYXdrA9aU-2hv_eprY' -BASE_URL = 'http://ws.audioscrobbler.com/2.0/?' - - -def _handle_albums(results) -> ItemList: - albums = ItemList(content_type="albums") - if not results: - return albums - if 'topalbums' in results and "album" in results['topalbums']: - for album in results['topalbums']['album']: - albums.append({'artist': album['artist']['name'], - 'mbid': album.get('mbid', ""), - 'mediatype': "album", - 'thumb': album['image'][-1]['#text'], - 'label': "%s - %s" % (album['artist']['name'], album['name']), - 'title': album['name']}) - albums.append(album) - return albums - - -def _handle_artists(results) -> ItemList: - artists = ItemList(content_type="artists") - if not results: - return artists - for artist in results['artist']: - if 'name' not in artist: - continue - artist = {'title': artist['name'], - 'label': artist['name'], - 'mediatype': "artist", - 'mbid': artist.get('mbid'), - 'thumb': artist['image'][-1]['#text'], - 'Listeners': format(int(artist.get('listeners', 0)), ",d")} - artists.append(artist) - return artists - - -def get_top_artists() -> ItemList: - """Queries LastFM api chart.getTopArtists method for top 100 artists - - Returns: - ItemList: a kutils object that wraps a list of artist - info dicts - """ - results: Optional[dict] = get_data(method="chart.getTopArtists", - params={"limit": "100"}) - return _handle_artists(results['artists']) - - -def get_artist_albums(artist_mbid: str) -> ItemList: - """Queries LastFM api artist.getTopAlbums method for an artist - - Gets 50 albums with title, mbid, and cover image - - Args: - artist_mbid (str): The musicbrainz id for the artist - - Returns: - ItemList: a kutils object that wraps a list of albums - info dicts - """ - if not artist_mbid: - return ItemList(content_type="albums") - results = get_data(method="artist.getTopAlbums", - params={"mbid": artist_mbid}) - return _handle_albums(results) - - -def get_similar_artists(artist_mbid: str) -> ItemList: - """Queries LastFM api artist.getsimilar for artists - - Gets name, mbid, and thumb image of similar artists - - Args: - artist_mbid (str): The musicbrainz id for the artist - - Returns: - ItemList: a kutils object that wraps a list of artists info dicts - """ - if not artist_mbid: - return ItemList(content_type="artists") - params = {"mbid": artist_mbid, - "limit": "400"} - results = get_data(method="artist.getSimilar", - params=params) - if results and "similarartists" in results: - return _handle_artists(results['similarartists']) - - -def get_track_info(artist_name="", track="") -> dict: - """ Queries LastFM api - - Args: - artist_name (str, optional): The artist name. Defaults to "". - track (str, optional): The track name. Defaults to "". - - Returns: - dict: LastFM info including scrobles of a song. - """ - if not artist_name or not track: - return {} - params = {"artist": artist_name, - "track": track} - results: Optional[dict] = get_data(method="track.getInfo", - params=params) - if not results: - return {} - summary = results['track']['wiki']['summary'] if "wiki" in results['track'] else "" - return {'playcount': str(results['track']['playcount']), - 'thumb': str(results['track']['playcount']), - 'summary': clean_text(summary)} - - -def get_data(method: str, params=None, cache_days=0.5) -> dict: - """helper function runs query including using local cache - - Args: - method (str): LastFM api method - params (dict, optional): LastFM method parameters. Defaults to None. - cache_days (float, optional): Days to use cache/query. Defaults to 0.5. - - Returns: - dict: The json.loads results from the query - """ - params = params if params else {} - params["method"] = method - params["api_key"] = LAST_FM_API_KEY - params["format"] = "json" - params = {k: str(v) for k, v in params.items() if v} - url = "{base_url}{params}".format(base_url=BASE_URL, - params=urllib.parse.urlencode(params)) - return utils.get_JSON_response(url=url, - cache_days=cache_days, - folder="LastFM") - - -def clean_text(text) -> str: - """Helper function to unescape chars - - Args: - text (str): text string to unescape - - Returns: - str: text string - """ - if not text: - return "" - text = re.sub( - '(From Wikipedia, the free encyclopedia)|(Description above from the Wikipedia.*?Wikipedia)', '', text) - text = re.sub('<(.|\n|\r)*?>', '', text) - text = text.replace('
', '[CR]') - text = text.replace('', '[I]').replace('', '[/I]') - text = text.replace('&', '&') - text = text.replace('>', '>').replace('<', '<') - text = text.replace(''', "'").replace('"', '"') - text = re.sub("\n\\.$", "", text) - text = text.replace( - 'User-contributed text is available under the Creative Commons By-SA License and may also be available under the GNU FDL.', '') - removals = {'\u200b', " ", "\n"} - while text: - s = text[0] - e = text[-1] - if s in removals: - text = text[1:] - elif e in removals: - text = text[:-1] - elif s.startswith(".") and not s.startswith(".."): - text = text[1:] - else: - break - return text.strip() diff --git a/script.extendedinfo/resources/lib/TheAudioDB.py b/script.extendedinfo/resources/lib/TheAudioDB.py deleted file mode 100644 index 6d3ca9258..000000000 --- a/script.extendedinfo/resources/lib/TheAudioDB.py +++ /dev/null @@ -1,268 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details -"""Modules with get_* functions to query TADB - -Requires user API key (subscription basis) to access -TODO: handle user api key - -""" - -import urllib.error -import urllib.parse -import urllib.request -from typing import Union - -import xbmc - -from kutils import AudioItem, ItemList, VideoItem, addon, local_db, utils - -AUDIO_DB_KEY = '58353d43204d68753987fl' #key no longer accepted -BASE_URL = 'https://www.theaudiodb.com/api/v1/json' -PLUGIN_BASE = 'plugin://script.extendedinfo/?info=' - - -def _handle_albums(results): - """[summary] - - Args: - results ([type]): [description] - - Returns: - [type]: [description] - """ - albums = ItemList(content_type="albums") - if not results.get('album'): - return albums - local_desc = 'strDescription' + xbmc.getLanguage(xbmc.ISO_639_1).upper() - for item in results['album']: - desc = "" - if local_desc in item and item[local_desc]: - desc = item.get(local_desc, "") - elif item.get('strDescriptionEN'): - desc = item['strDescriptionEN'] - elif item.get('strDescription'): - desc = item['strDescription'] - if item.get('strReview'): - desc += "[CR][CR][B]%s:[/B][CR][CR]%s" % ( - addon.LANG(185), item['strReview']) - album = AudioItem(label=item['strAlbum'], - path="") - album.set_infos({'artist': item['strArtist'], - 'album': item['strAlbum'], - 'mediatype': "album", - 'genre': item['strGenre'], - 'year': item['intYearReleased']}) - album.set_properties({'mbid': item['strMusicBrainzID'], - 'id': item['idAlbum'], - 'audiodb_id': item['idAlbum'], - 'album_description': desc, - 'album_mood': item['strMood'], - 'album_style': item['strStyle'], - 'speed': item['strSpeed'], - 'album_Theme': item['strTheme'], - 'type': item['strReleaseFormat'], - 'loved': item['intLoved'], - 'location': item['strLocation'], - 'itunes_id': item['strItunesID'], - 'amazon_id': item['strAmazonID'], - 'sales': item['intSales']}) - album.set_artwork({'thumb': item['strAlbumThumb'], - 'spine': item['strAlbumSpine'], - 'cdart': item['strAlbumCDart'], - 'thumbback': item['strAlbumThumbBack']}) - albums.append(album) - return local_db.compare_album_with_library(albums) - - -def _handle_tracks(results: dict) -> ItemList: - tracks = ItemList(content_type="songs") - if not results.get('track'): - return tracks - for item in results['track']: - youtube_id = utils.extract_youtube_id(item.get('strMusicVid', '')) - track = AudioItem(label=item['strTrack'], - path="%syoutubevideo&&id=%s" % (PLUGIN_BASE, youtube_id)) - track.set_infos({'title': item['strTrack'], - 'album': item['strAlbum'], - 'artist': item['strArtist'], - 'mediatype': "song"}) - track.set_properties({'mbid': item['strMusicBrainzID']}) - track.set_artwork( - {'thumb': "http://i.ytimg.com/vi/%s/0.jpg" % youtube_id}) - tracks.append(track) - return tracks - - -def _handle_musicvideos(results): - mvids = ItemList(content_type="musicvideos") - if not results.get('mvids'): - return mvids - for item in results['mvids']: - youtube_id = utils.extract_youtube_id(item.get('strMusicVid', '')) - mvid = VideoItem(label=item['strTrack'], - path="%syoutubevideo&&id=%s" % (PLUGIN_BASE, youtube_id)) - mvid.set_infos({'title': item['strTrack'], - 'plot': item['strDescriptionEN'], - 'mediatype': "musicvideo"}) - mvid.set_properties({'id': item['idTrack']}) - mvid.set_artwork( - {'thumb': "http://i.ytimg.com/vi/%s/0.jpg" % youtube_id}) - mvids.append(mvid) - return mvids - - -def extended_artist_info(results: dict) -> dict: - if not results.get('artists'): - return {} - local_bio = 'strBiography' + addon.setting("LanguageID").upper() - artist = results['artists'][0] - description = "" - if local_bio in artist and artist[local_bio]: - description = artist.get(local_bio) - elif artist.get('strBiographyEN'): - description = artist.get('strBiographyEN') - elif artist.get('strBiography'): - description = artist.get('strBiography') - if 'strReview' in artist and artist['strReview']: - description += "[CR]" + artist.get('strReview') - artist = {'label': artist.get('strArtist'), - 'artist': artist.get('strArtist'), - 'mediatype': "artist", - 'Country': artist.get('strCountry'), - 'mbid': artist.get('strMusicBrainzID'), - 'thumb': artist.get('strArtistThumb'), - 'Banner': artist.get('strArtistBanner'), - 'clearlogo': artist.get('strArtistLogo'), - 'fanart': artist.get('strArtistFanart'), - 'fanart2': artist.get('strArtistFanart2'), - 'fanart3': artist.get('strArtistFanart3'), - 'Artist_Mood': artist.get('strMood'), - 'Artist_Born': artist.get('intBornYear'), - 'Artist_Formed': artist.get('intFormedYear'), - 'Artist_Died': artist.get('intDiedYear'), - 'Artist_Disbanded': artist.get('strDisbanded'), - 'Artist_Mood': artist.get('strMood'), - 'Artist_Description': description, - 'Artist_Genre': artist.get('strGenre'), - 'Artist_Style': artist.get('strStyle'), - 'CountryCode': artist.get('strCountryCode'), - 'Website': artist.get('strWebsite'), - 'Twitter': artist.get('strTwitter'), - 'Facebook': artist.get('strFacebook'), - 'LastFMChart': artist.get('strLastFMChart'), - 'Gender': artist.get('strGender'), - 'audiodb_id': artist.get('idArtist'), - 'Members': artist.get('intMembers')} - return artist - - -def get_artist_discography(search_str) -> ItemList: - """returns artist's discography - - Args: - search_str (str): Artist name - - Returns: - [ItemList]: Kutils list instance of AudioItems - """ - if not search_str: - return ItemList(content_type="albums") - params: dict = {"s": search_str} - results: dict = get_data("searchalbum", params) - return _handle_albums(results) - - -def get_artist_details(search_str) -> Union[ItemList, dict]: - """gets artist details from TADB - - Args: - search_str [str]: artist name - - Returns: - Union[ItemList, dict]: the extended artist info - """ - if not search_str: - return ItemList(content_type="artists") - params = {"s": search_str} - results = get_data("search", params) - return extended_artist_info(results) - - -def get_most_loved_tracks(search_str="", mbid="") -> Union[ItemList, list]: - """ highest rated TADB soings for artist - - Args: - search_str (str, optional): artist name. Defaults to "". - mbid (str, optional): musicbrainz artist id. Defaults to "". - - Returns: - Union[ItemList, list]: list of songs - """ - if mbid: - url = 'track-top10-mb' - params = {"s": mbid} - elif search_str: - url = 'track-top10' - params = {"s": search_str} - else: - return [] - results = get_data(url, params) - return _handle_tracks(results) - - -def get_album_details(audiodb_id="", mbid=""): - if audiodb_id: - url = 'album' - params = {"m": audiodb_id} - elif mbid: - url = 'album-mb' - params = {"i": mbid} - else: - return [] - results = get_data(url, params) - return _handle_albums(results)[0] - - -def get_musicvideos(audiodb_id): - if not audiodb_id: - return ItemList(content_type="musicvideos") - params = {"i": audiodb_id} - results = get_data("mvid", params) - return _handle_musicvideos(results) - - -def get_track_details(audiodb_id: str) -> Union[ItemList, list]: - """gets TADB info for a track - - Args: - audiodb_id (str): The TADB id - - Returns: - Union[ItemList, list]: List of track details - """ - if not audiodb_id: - return ItemList(content_type="songs") - params = {"m": audiodb_id} - results = get_data("track", params) - return _handle_tracks(results) - - -def get_data(url: str, params: dict) -> dict: - """returns a dict from TADB api query - - Args: - url (str): the TADB GET query - params (dict): TADB query pamaeters - - Returns: - dict: TADB api response - """ - tadb_key: str = addon.setting('TADB API Key') - params: dict = {k: str(v) for k, v in params.items() if v} - url: str = "{0}/{1}/{2}.php?{3}".format(BASE_URL, - tadb_key, url, urllib.parse.urlencode(params)) - return utils.get_JSON_response(url=url, - folder="TheAudioDB") diff --git a/script.extendedinfo/resources/lib/TheMovieDB.py b/script.extendedinfo/resources/lib/TheMovieDB.py deleted file mode 100644 index 3a6204217..000000000 --- a/script.extendedinfo/resources/lib/TheMovieDB.py +++ /dev/null @@ -1,1353 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import re -import urllib.error -import urllib.parse -import urllib.request -from typing import List, Optional, Union, Tuple - -from kutils import (ItemList, VideoItem, addon, kodijson, local_db, - selectdialog, utils) - -TMDB_KEY = '34142515d9d23817496eeb4ff1d223d0' -POSTER_SIZES = ["w92", "w154", "w185", "w342", "w500", "w780", "original"] -LOGO_SIZES = ["w45", "w92", "w154", "w185", "w300", "w500", "original"] -BACKDROP_SIZES = ["w300", "w780", "w1280", "original"] -PROFILE_SIZES = ["w45", "w185", "h632", "original"] -STILL_SIZES = ["w92", "w185", "w300", "original"] -HEADERS = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'User-agent': 'Kodi/17.0 ( phil65@kodi.tv )' -} -IMAGE_BASE_URL = "http://image.tmdb.org/t/p/" -POSTER_SIZE = "w500" -URL_BASE = "https://api.themoviedb.org/3/" -ALL_MOVIE_PROPS = "account_states,alternative_titles,credits,images,keywords,release_dates,videos,translations,similar,reviews,lists,rating" -ALL_TV_PROPS = "account_states,alternative_titles,content_ratings,credits,external_ids,images,keywords,rating,similar,translations,videos" -ALL_ACTOR_PROPS = "tv_credits,movie_credits,combined_credits,images,tagged_images" -ALL_SEASON_PROPS = "videos,images,external_ids,credits" -ALL_EPISODE_PROPS = "account_states,credits,external_ids,images,rating,videos" - -PLUGIN_BASE = "plugin://script.extendedinfo/?info=" - -RELEASE_TYPES = {1: "Premiere", - 2: "Theatrical (limited)", - 3: "Theatrical", - 4: "Digital", - 5: "Physical", - 6: "TV"} - -GENDERS = {1: addon.LANG(32095), - 2: addon.LANG(32094)} - -STATUS = {"released": addon.LANG(32071), - "post production": addon.LANG(32072), - "in production": addon.LANG(32073), - "ended": addon.LANG(32074), - "returning series": addon.LANG(32075), - "planned": addon.LANG(32076)} - - -class LoginProvider: - """ - logs into TMDB for user or guest and gets corresponding session or guest session id - """ - - def __init__(self, *args, **kwargs): - self.session_id = None - self.logged_in = False - self.account_id = None - self.username = kwargs.get("username") - self.password = kwargs.get("password") - - def check_login(self) -> bool: - """determines if user has an active login (session id) on tmdb when opening a tmdb-based - dialog - see https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id - for the tmdb protocol - Note: in api v4 this will become mandatory. Optional in v3 - - Returns: - bool: true if user has an active session id from tmdb - """ - if self.username and self.password: - return bool(self.get_session_id()) - return False - - def get_account_id(self) -> str: - """returns TMDB account id - - Returns: - str: the tmdb account id or None - """ - if self.account_id: - return self.account_id - self.session_id = self.get_session_id() - response = get_data(url="account", - params={"session_id": self.session_id}, - cache_days=999999) - if not response: - return None - self.account_id = response.get("id") - return self.account_id - - def get_guest_session_id(self) -> str: - """returns guest session id for TMDB guest session has limited privledge - - Returns: - str: tmdb guest session id or None - """ - response = get_data(url="authentication/guest_session/new", - cache_days=9999) - if not response or "guest_session_id" not in response: - return None - return str(response["guest_session_id"]) - - def get_session_id(self, cache_days=999) -> str: - """gets the tmdb session id from addon settings or creates one if not found - - Args: - cache_days (int, optional): Days to cache a session id in settings. - Defaults to 999. - - Returns: - str: the tmdb session id - """ - if addon.setting("session_id"): - return addon.setting("session_id") - self.create_session_id() - return self.session_id - - def create_session_id(self) -> None: - """gets session id from tmdb as self.session_id and saves it in addon settings - """ - response = get_data(url="authentication/token/new", - cache_days=0) - params = {"request_token": response["request_token"], - "username": self.username, - "password": self.password} - response = get_data(url="authentication/token/validate_with_login", - params=params, - cache_days=0) - if response and response.get("success"): - request_token = response["request_token"] - response = get_data(url="authentication/session/new", - params={"request_token": request_token}) - if response and "success" in response: - self.session_id = str(response["session_id"]) - addon.set_setting("session_id", self.session_id) - - -def set_rating(media_type, media_id, rating, dbid=None): - ''' - media_type: movie, tv or episode - media_id: tmdb_id / episode ident array - rating: ratung value (1 - 10, 0 for deleting) - dbid: dbid for syncing userrating of db item - ''' - if not media_type or not media_id or rating == -1: - return False - if dbid: - kodijson.set_userrating(media_type, dbid, rating) - params = {} - if Login.check_login(): - params["session_id"] = Login.get_session_id() - else: - params["guest_session_id"] = Login.get_guest_session_id() - if media_type == "episode": - if not media_id[1]: - media_id[1] = "0" - url = "tv/%s/season/%s/episode/%s/rating" % ( - media_id[0], media_id[1], media_id[2]) - else: - url = "%s/%s/rating" % (media_type, media_id) - results = send_request(url=url, - params=params, - values={"value": "%.1f" % - float(rating)} if rating > 0 else {}, - delete=rating == 0) - if results: - utils.notify(addon.NAME, results["status_message"]) - return True - - -def send_request(url, params, values, delete=False): - params["api_key"] = TMDB_KEY - params = {k: str(v) for k, v in params.items() if v} - url = "%s%s?%s" % (URL_BASE, url, urllib.parse.urlencode(params)) - #utils.log(url) - if delete: - return utils.delete(url, values=values, headers=HEADERS) - else: - return utils.post(url, values=values, headers=HEADERS) - - -def change_fav_status(media_id=None, media_type="movie", status="true"): - session_id = Login.get_session_id() - if not session_id: - utils.notify("Could not get session id") - return None - values = {"media_type": media_type, - "media_id": media_id, - "favorite": status} - results = send_request(url="account/%s/favorite" % Login.get_account_id(), - params={"session_id": session_id}, - values=values) - if results: - utils.notify(addon.NAME, results["status_message"]) - - -def create_list(list_name): - ''' - creates new list on TMDB with name *list_name - returns newly created list id - ''' - values = {'name': list_name, - 'description': 'List created by ExtendedInfo Script for Kodi.'} - results = send_request(url="list", - params={"session_id": Login.get_session_id()}, - values=values) - if results: - utils.notify(addon.NAME, results["status_message"]) - return results["list_id"] - - -def remove_list_dialog(account_lists): - index = selectdialog.open(header=addon.LANG(32138), - listitems=account_lists) - if index >= 0: - remove_list(account_lists[index]["id"]) - return True - - -def remove_list(list_id): - results = send_request(url="list/%s" % list_id, - params={"session_id": Login.get_session_id()}, - values={'media_id': list_id}, - delete=True) - if results: - utils.notify(addon.NAME, results["status_message"]) - return results["list_id"] - - -def change_list_status(list_id, movie_id, status): - method = "add_item" if status else "remove_item" - results = send_request(url="list/%s/%s" % (list_id, method), - params={"session_id": Login.get_session_id()}, - values={'media_id': movie_id}) - if results: - utils.notify(addon.NAME, results["status_message"]) - - -def get_account_lists(cache_days=0): - ''' - returns movie lists for TMDB user - ''' - session_id = Login.get_session_id() - account_id = Login.get_account_id() - if not session_id or not account_id: - return [] - response = get_data(url="account/%s/lists" % (account_id), - params={"session_id": session_id}, - cache_days=cache_days) - return response["results"] - - -def get_certification_list(media_type): - response = get_data(url="certification/%s/list" % media_type, - cache_days=999999) - return response.get("certifications") - - -def merge_with_cert_desc(input_list, media_type): - cert_list = get_certification_list(media_type) - for item in input_list: - iso = item.get_property("iso_3166_1").upper() - if iso not in cert_list: - continue - hit = utils.dictfind(lst=cert_list[iso], - key="certification", - value=item.get_property("certification")) - if hit: - item.set_property("meaning", hit["meaning"]) - return input_list - - -def handle_multi_search(results): - listitems = ItemList(content_type="videos") - for item in results: - if item["media_type"] == "movie": - listitems.append(handle_movies([item])[0]) - elif item["media_type"] == "tv": - listitems.append(handle_tvshows([item])[0]) - elif item["media_type"] == "person": - listitems.append(handle_people([item])[0]) - return listitems - - -def handle_movies(results: List[dict], local_first=True, sortkey="year") ->ItemList: - """takes a list of movies (dicts) and adds local db data then sorts as an ItemList - - Args: - results (List[dict]): a list of movies, each movie is a dict - local_first (bool, optional): should movies in the db list first. Defaults to True. - sortkey (str, optional): key to sort the movies. Defaults to "year". - - Returns: - ItemList: a kutils ItemList of the movies to display in a Kodi container - """ - response: dict = get_data(url="genre/movie/list", - params={"language": addon.setting("LanguageID")}, - cache_days=30) - ids: List[int] = [item["id"] for item in response["genres"]] - labels: List[str] = [item["name"] for item in response["genres"]] - movies = ItemList(content_type="movies") - path = 'extendedinfo&&id=%s' if addon.bool_setting( - "infodialog_onclick") else "playtrailer&&id=%s" - for movie in results: - genres = [labels[ids.index(id_)] for id_ in movie.get( - "genre_ids", []) if id_ in ids] - item = VideoItem(label=movie.get('title'), - path=PLUGIN_BASE + path % movie.get("id")) - release_date = movie.get('release_date') - item.set_infos({'title': movie.get('title'), - 'originaltitle': movie.get('original_title', ""), - 'mediatype': "movie", - 'country': movie.get('original_language'), - 'plot': movie.get('overview'), - 'Trailer': "%splaytrailer&&id=%s" % (PLUGIN_BASE, movie.get("id")), - 'genre': " / ".join([i for i in genres if i]), - 'votes': movie.get('vote_count'), - 'year': utils.get_year(release_date), - 'rating': round(movie['vote_average'], 1) if movie.get('vote_average') else "", - 'userrating': movie.get('rating'), - 'premiered': release_date}) - item.set_properties({'id': movie.get("id"), - 'popularity': round(movie['popularity'], 1) if movie.get('popularity') else "", - 'credit_id': movie.get('credit_id'), - 'character': movie.get('character'), - 'job': movie.get('job'), - 'department': movie.get('department')}) - item.set_artwork(get_image_urls(poster=movie.get("poster_path"), - fanart=movie.get("backdrop_path"))) - movies.append(item) - return local_db.merge_with_local(media_type="movie", - items=movies, - library_first=local_first, - sortkey=sortkey) - - -def handle_tvshows(results, local_first=True, sortkey="year"): - tvshows = ItemList(content_type="tvshows") - response = get_data(url="genre/tv/list", - params={"language": addon.setting("LanguageID")}, - cache_days=30) - ids = [item["id"] for item in response["genres"]] - labels = [item["name"] for item in response["genres"]] - for tv in results: - tmdb_id = tv.get("id") - genres = [labels[ids.index(id_)] - for id_ in tv.get("genre_ids", []) if id_ in ids] - duration = "" - if "episode_run_time" in tv: - if len(tv["episode_run_time"]) > 1: - duration = "%i - %i" % (min(tv["episode_run_time"]), - max(tv["episode_run_time"])) - elif len(tv["episode_run_time"]) == 1: - duration = "%i" % (tv["episode_run_time"][0]) - newtv = VideoItem(label=tv.get('name'), - path=PLUGIN_BASE + 'extendedtvinfo&&id=%s' % tmdb_id) - newtv.set_infos({'originaltitle': tv.get('original_name', ""), - 'title': tv.get('name'), - 'duration': duration, - 'genre': " / ".join([i for i in genres if i]), - 'country': tv.get('original_language'), - 'plot': tv.get("overview"), - 'year': utils.get_year(tv.get('first_air_date')), - 'mediatype': "tvshow", - 'rating': round(tv['vote_average'], 1) if tv.get("vote_average") else "", - 'userrating': tv.get('rating'), - 'votes': tv.get('vote_count'), - 'premiered': tv.get('first_air_date')}) - newtv.set_properties({'id': tmdb_id, - 'character': tv.get('character'), - 'popularity': round(tv['popularity'], 1) if tv.get('popularity') else "", - 'credit_id': tv.get('credit_id'), - 'totalepisodes': tv.get('number_of_episodes'), - 'totalseasons': tv.get('number_of_seasons')}) - newtv.set_artwork(get_image_urls(poster=tv.get("poster_path"), - fanart=tv.get("backdrop_path"))) - tvshows.append(newtv) - tvshows = local_db.merge_with_local(media_type="tvshow", - items=tvshows, - library_first=local_first, - sortkey=sortkey) - return tvshows - - -def handle_episodes(results): - listitems = ItemList(content_type="episodes") - for item in results: - title = item.get("name") - if not title: - title = "%s %s" % (addon.LANG(20359), item.get('episode_number')) - listitem = {'label': title} - listitem = VideoItem(label=title, - artwork=get_image_urls(still=item.get("still_path"))) - listitem.set_infos({'mediatype': "episode", - 'title': title, - 'aired': item.get('air_date'), - 'episode': item.get('episode_number'), - 'season': item.get('season_number'), - 'code': item.get("production_code"), - 'userrating': item.get('rating'), - 'plot': item.get('overview'), - 'rating': round(item['vote_average'], 1) if item.get('vote_average') else "", - 'votes': item.get('vote_count')}) - listitem.set_properties({'id': item.get('id'), - 'production_code': item.get('production_code')}) - listitems.append(listitem) - return listitems - - -def handle_release_dates(results): - listitems = ItemList() - for item in results: - ref = item["release_dates"][0] - if not ref.get("certification"): - continue - listitem = VideoItem(label=item.get('name')) - listitem.set_properties({'certification': ref.get('certification'), - 'iso_3166_1': item.get('iso_3166_1', "").lower(), - 'note': ref.get('note'), - 'iso_639_1': ref.get('iso_639_1'), - 'release_date': ref.get('release_date'), - 'type': RELEASE_TYPES.get(ref.get('type'))}) - listitems.append(listitem) - return listitems - - -def handle_content_ratings(results): - listitems = ItemList() - for item in results: - listitem = VideoItem(label=item['rating']) - listitem.set_properties({'iso_3166_1': item['iso_3166_1'].lower(), - 'certification': item['rating'].lower()}) - listitems.append(listitem) - return listitems - - -def handle_reviews(results): - listitems = ItemList() - for item in results: - listitem = VideoItem(label=item.get('author')) - listitem.set_properties({'author': item.get('author'), - 'content': re.sub("", "", item.get('content')).lstrip(), - 'id': item.get('id'), - 'url': item.get('url')}) - listitems.append(listitem) - return listitems - - -def handle_text(results): - listitems = ItemList() - for item in results: - listitem = VideoItem(label=item.get('name')) - listitem.set_property("id", item.get('id')) - listitems.append(listitem) - return listitems - - -def handle_lists(results): - listitems = ItemList(content_type="sets") - for item in results: - listitem = VideoItem(label=item.get('name'), - path="plugin://script.extendedinfo?info=listmovies&---id=%s" % item.get( - 'id'), - artwork=get_image_urls(poster=item.get("poster_path"))) - listitem.set_infos({'plot': item.get('description'), - "media_type": "set"}) - listitem.set_properties({'certification': item.get('certification', "") + item.get('rating', ""), - 'item_count': item.get('item_count'), - 'favorite_count': item.get('favorite_count'), - 'iso_3166_1': item.get('iso_3166_1', "").lower(), - 'id': item.get('id')}) - listitems.append(listitem) - return listitems - - -def handle_seasons(results): - listitems = ItemList(content_type="seasons") - for item in results: - season = item.get('season_number') - listitem = VideoItem(label=addon.LANG(20381) if season == 0 else "%s %s" % (addon.LANG(20373), season), - properties={'id': item.get('id')}, - artwork=get_image_urls(poster=item.get("poster_path"))) - listitem.set_infos({'mediatype': "season", - 'season': season, - 'premiered': item.get('air_date'), - 'year': utils.get_year(item.get('air_date'))}) - listitems.append(listitem) - return listitems - - -def handle_videos(results): - listitems = ItemList(content_type="videos") - for item in results: - listitem = VideoItem(label=item.get('name'), - size=item.get('size'), - artwork={'thumb': "http://i.ytimg.com/vi/%s/0.jpg" % item.get('key')}) - listitem.set_infos({'mediatype': "video"}) - listitem.set_properties({'iso_639_1': item.get('iso_639_1'), - 'type': item.get('type'), - 'key': item.get('key'), - 'youtube_id': item.get('key'), - 'site': item.get('site'), - 'id': item.get('id')}) - listitems.append(listitem) - return listitems - - -def handle_people(results): - people = ItemList(content_type="actors") - for item in results: - person = VideoItem(label=item['name'], - path="%sextendedactorinfo&&id=%s" % ( - PLUGIN_BASE, item['id']), - infos={'mediatype': "artist"}, - artwork=get_image_urls(profile=item.get("profile_path"))) - person.set_properties({'adult': item.get('adult'), - 'alsoknownas': " / ".join(item.get('also_known_as', [])), - 'biography': item.get('biography'), - 'birthday': item.get('birthday'), - 'age': utils.calculate_age(item.get('birthday'), item.get('deathday')), - 'character': item.get('character'), - 'department': item.get('department'), - 'job': item.get('job'), - 'id': item['id'], - 'cast_id': item.get('cast_id'), - 'credit_id': item.get('credit_id'), - 'deathday': item.get('deathday'), - 'placeofbirth': item.get('place_of_birth'), - 'homepage': item.get('homepage')}) - people.append(person) - return people - - -def handle_images(results): - images = ItemList(content_type="images") - for item in results: - artwork = get_image_urls(poster=item.get("file_path")) - image = VideoItem(artwork=artwork) - image.set_properties({'aspectratio': item['aspect_ratio'], - 'type': "poster" if item['aspect_ratio'] < 0.7 else "fanart", - 'rating': item.get("vote_average"), - 'votes': item.get("vote_count"), - 'iso_639_1': item.get("iso_639_1")}) - if item.get("media"): - image.set_label(item["media"].get("title")) - image.set_property("movie_id", item["media"].get("id")) - poster_path = item["media"].get("poster_path") - if poster_path: - image.update_artwork( - {'mediaposter': IMAGE_BASE_URL + POSTER_SIZE + poster_path}) - image.set_info("mediatype", "music") - images.append(image) - return images - - -def handle_companies(results): - companies = ItemList(content_type="studios") - for item in results: - company = VideoItem(label=item['name'], - infos={'plot': item.get('description')}) - company.set_properties({'parent_company': item.get('parent_company'), - 'headquarters': item.get('headquarters'), - 'homepage': item.get('homepage'), - 'id': item['id']}) - art = "resource://resource.images.studios.white/{}.png".format( - item['name']) if item['name'] else "" - company.set_artwork({"thumb": art, - "icon": art}) - companies.append(company) - return companies - - -def search_companies(company_name): - regex = re.compile('\(.+?\)') - response = get_data(url="search/company", - params={"query": regex.sub('', company_name)}, - cache_days=10) - if response and "results" in response: - return handle_companies(response["results"]) - else: - utils.log("Could not find company ID for %s" % company_name) - return None - - -def multi_search(search_str, page=1, cache_days=1): - params = {"query": search_str, - "include_adult": addon.setting("include_adults").lower(), - "page": page} - response = get_data(url="search/multi", - params=params, - cache_days=cache_days) - if response and "results" in response: - itemlist = handle_multi_search(response["results"]) - itemlist.set_totals(response["total_results"]) - return itemlist - - -def get_list_movies(list_id, force): - url = "list/%s" % (list_id) - response = get_data(url=url, - params={"language": addon.setting("LanguageID")}, - cache_days=0 if force else 2) - if not response: - return None - items = handle_movies(results=response["items"], - local_first=True, - sortkey=None) - itemlist = ItemList(items=items) - itemlist.set_totals(len(response["items"])) - return itemlist - - -def get_person_info(person_label, skip_dialog=False): - if not person_label: - return False - params = {"query": person_label.split(" / ")[0], - "include_adult": addon.setting("include_adults").lower()} - response = get_data(url="search/person", - params=params, - cache_days=30) - if not response or "results" not in response: - return False - people = [i for i in response["results"] if i["name"] == person_label] - if len(people) > 1 and not skip_dialog: - index = selectdialog.open(header=f'{addon.LANG(32151)} TMDB People', - listitems=handle_people(people)) - return people[index] if index > -1 else False - elif people: - return people[0] - elif response["results"]: - return response["results"][0] - return False - - -def get_keywords(search_label): - params = {"query": search_label, - "include_adult": addon.setting("include_adults").lower()} - response = get_data(url="search/keyword", - params=params, - cache_days=30) - if not response or not response.get("results"): - utils.log("could not find Keyword ID") - return False - return response["results"] - - -def get_set_id(set_name): - params = {"query": set_name.replace("[", "").replace("]", "").replace("Kollektion", "Collection"), - "language": addon.setting("LanguageID")} - response = get_data(url="search/collection", - params=params, - cache_days=14) - if not response or not response.get("results"): - return "" - return response["results"][0]["id"] - - -def get_data(url: str = "", params: Optional[dict] = None, cache_days: float = 14) -> Optional[dict]: - """Queries tmdb api v3 or local cache - - Args: - url (str, optional): tmdb query terms for the search apiv3. Defaults to "". - params (Optional[dict], optional): Dict of optional parameters for - query. Defaults to None. - cache_days (float, optional): Days to check for cached values. - Defaults to 14. - - Returns: - dict: A dict of JSON.loads response from TMDB or None if no - TMDB response - """ - params = params if params else {} - params["api_key"] = TMDB_KEY - params = {k: str(v) for k, v in params.items() if v} - url = "%s%s?%s" % (URL_BASE, url, urllib.parse.urlencode(params)) - response = utils.get_JSON_response(url, cache_days, "TheMovieDB") - if not response: - utils.log("No response from TMDB") - return None - elif "status_code" in response: - utils.log("TMDB status code: %s" % response.get("status_code")) - return response - - -def get_company_data(company_id): - if not company_id: - return [] - response = get_data(url="company/%s/movies" % (company_id), - cache_days=30) - if not response or not response.get("results"): - return [] - return handle_movies(response["results"]) - - -def get_credit_info(credit_id): - if not credit_id: - return [] - return get_data(url="credit/%s" % (credit_id), - params={"language": addon.setting("LanguageID")}, - cache_days=30) - - -def get_account_props(states): - return {"FavButton_Label": addon.LANG(32155) if states.get("favorite") else addon.LANG(32154), - "favorite": "True" if states.get("favorite") else "", - "rated": int(states["rated"]["value"]) if states["rated"] else "", - "watchlist": states["watchlist"] if "watchlist" in states else ""} - - -def get_image_urls(poster=None, still=None, fanart=None, profile=None): - ''' - get a dict with all available images for given image types - ''' - images = {} - if poster: - images["poster"] = IMAGE_BASE_URL + "w500" + poster - images["poster_original"] = IMAGE_BASE_URL + "original" + poster - images["original"] = IMAGE_BASE_URL + "original" + poster - images["poster_small"] = IMAGE_BASE_URL + "w342" + poster - images["thumb"] = IMAGE_BASE_URL + "w342" + poster - if still: - images["thumb"] = IMAGE_BASE_URL + "w300" + still - images["still"] = IMAGE_BASE_URL + "w300" + still - images["still_original"] = IMAGE_BASE_URL + "original" + still - images["still_small"] = IMAGE_BASE_URL + "w185" + still - if fanart: - images["fanart"] = IMAGE_BASE_URL + "w1280" + fanart - images["fanart_original"] = IMAGE_BASE_URL + "original" + fanart - images["original"] = IMAGE_BASE_URL + "original" + fanart - images["fanart_small"] = IMAGE_BASE_URL + "w780" + fanart - if profile: - images["poster"] = IMAGE_BASE_URL + "w500" + profile - images["poster_original"] = IMAGE_BASE_URL + "original" + profile - images["poster_small"] = IMAGE_BASE_URL + "w342" + profile - images["thumb"] = IMAGE_BASE_URL + "w342" + profile - return images - - -def get_movie_tmdb_id(imdb_id=None, name=None, dbid=None): - if dbid and (int(dbid) > 0): - movie_id = local_db.get_imdb_id("movie", dbid) - if movie_id: - utils.log("IMDB Id from local DB: %s" % (movie_id)) - return movie_id - if imdb_id: - params = {"external_source": "imdb_id", - "language": addon.setting("LanguageID")} - response = get_data(url="find/tt%s" % (imdb_id.replace("tt", "")), - params=params) - if response and response["movie_results"]: - return response["movie_results"][0]["id"] - return search_media(name) if name else None - - -def get_show_tmdb_id(tvdb_id=None, source="tvdb_id"): - params = {"external_source": source, - "language": addon.setting("LanguageID")} - response = get_data(url="find/%s" % (tvdb_id), - params=params) - if not response or not response["tv_results"]: - utils.notify("TVShow Info not available.") - return None - return response["tv_results"][0]["id"] - - -def get_show_id(tmdb_id=None, return_id="imdb_id"): - params = {"append_to_response": "external_ids", - "language": addon.setting("LanguageID")} - response = get_data(url="tv/%s" % (tmdb_id), - params=params) - if not response: - utils.notify("TVShow Info not available.") - return None - return response["external_ids"][return_id] - - -def get_movie_videos(movie_id): - ''' - get trailers / teasers for movie with *movie_id - ''' - response = get_movie(movie_id) - if response and "videos" in response and response['videos']['results']: - return response['videos']['results'] - utils.notify("Could not get movie videos") - return None - - -def extended_movie_info(movie_id=None, dbid=None, cache_days=14) -> Optional[dict]: - """get listitem with extended info for movie with *movie_id - merge in info from *dbid if available - - Args: - movie_id (str, optional): TMDB movie id. Defaults to None. - dbid (int, optional): Local library dbid. Defaults to None. - cache_days (int, optional): Days to use cached info. Defaults to 14. - - Returns: - Optional[dict]: A dict of movie information - """ - if not movie_id: - return None - info: Union[dict, None] = get_movie( - movie_id=movie_id, cache_days=cache_days) - if not info: - utils.notify("Could not get movie information") - return {} - mpaa = "" - studio = [i["name"] for i in info["production_companies"]] - authors = [i["name"] for i in info['credits'] - ['crew'] if i["department"] == "Writing"] - directors = [i["name"] for i in info['credits'] - ['crew'] if i["department"] == "Directing"] - us_cert = utils.dictfind( - info['release_dates']['results'], "iso_3166_1", "US") - if us_cert: - mpaa = us_cert['release_dates'][0]["certification"] - elif info['release_dates']['results']: - mpaa = info['release_dates']['results'][0]['release_dates'][0]['certification'] - movie_set = info.get("belongs_to_collection") - movie = VideoItem(label=info.get('title'), - path=PLUGIN_BASE + 'youtubevideo&&id=%s' % info.get("id", "")) - movie.set_infos({'title': info.get('title'), - 'tagline': info.get('tagline'), - 'duration': info.get('runtime'), - 'mpaa': mpaa, - 'director': " / ".join(directors), - 'writer': " / ".join(authors), - 'plot': info.get('overview'), - 'originaltitle': info.get('original_title'), - 'Country': info.get('original_language'), - 'imdbnumber': info.get('imdb_id'), - 'genre': " / ".join([i["name"] for i in info["genres"]]), - 'year': utils.get_year(info.get("release_date")), - 'rating': round(info['vote_average'], 1) if info.get('vote_average') else "", - 'premiered': info.get('release_date'), - 'votes': info.get('vote_count'), - 'studio': " / ".join(studio), - 'status': translate_status(info.get('status'))}) - movie.set_properties({'adult': str(info.get('adult')), - 'popularity': round(info['popularity'], 1) if info.get('popularity') else "", - 'set': movie_set.get("name") if movie_set else "", - 'set_id': movie_set.get("id") if movie_set else "", - 'id': info.get('id'), - 'imdb_id': info.get('imdb_id'), - 'budget': utils.millify(info.get("budget")), - 'revenue': utils.millify(info.get("revenue")), - 'homepage': info.get('homepage')}) - movie.set_artwork(get_image_urls(poster=info.get("poster_path"), - fanart=info.get("backdrop_path"))) - videos = handle_videos(info["videos"]["results"] - ) if "videos" in info else [] - account_states = info.get("account_states") - if dbid: - local_item = local_db.get_movie(dbid) - movie.update_from_listitem(local_item) - else: - movie = local_db.merge_with_local("movie", [movie])[0] - # hack to get tmdb rating instead of local one - movie.set_info("rating", round( - info['vote_average'], 1) if info.get('vote_average') else "") - releases = merge_with_cert_desc(handle_release_dates( - info["release_dates"]["results"]), "movie") - listitems = {"actors": handle_people(info["credits"]["cast"]), - "similar": handle_movies(info["similar"]["results"]), - "lists": sort_lists(handle_lists(info["lists"]["results"])), - "studios": handle_companies(info["production_companies"]), - "releases": releases, - "crew": handle_people(info["credits"]["crew"]).reduce(), - "genres": handle_text(info["genres"]), - "keywords": handle_text(info["keywords"]["keywords"]), - "reviews": handle_reviews(info["reviews"]["results"]), - "videos": videos, - "images": handle_images(info["images"]["posters"]), - "backdrops": handle_images(info["images"]["backdrops"])} - return (movie, listitems, account_states) - - -def get_tvshow(tvshow_id, cache_days=30, light=False): - if not tvshow_id: - return None - params = {"append_to_response": None if light else ALL_TV_PROPS, - "language": addon.setting("LanguageID"), - "include_image_language": "en,null,%s" % addon.setting("LanguageID")} - if Login.check_login(): - params["session_id"] = Login.get_session_id() - return get_data(url="tv/%s" % (tvshow_id), - params=params, - cache_days=cache_days) - - -def extended_tvshow_info(tvshow_id=None, cache_days=7, dbid=None): - ''' - get listitem with extended info for tvshow with *tvshow_id - merge in info from *dbid if available - ''' - info = get_tvshow(tvshow_id, cache_days) - if not info: - return False - account_states = info.get("account_states") - videos = handle_videos(info["videos"]["results"] - ) if "videos" in info else [] - tmdb_id = info.get("id", "") - if len(info.get("episode_run_time", -1)) > 1: - duration = "%i - %i" % (min(info["episode_run_time"]), - max(info["episode_run_time"])) - elif len(info.get("episode_run_time", -1)) == 1: - duration = "%i" % (info["episode_run_time"][0]) - else: - duration = "" - mpaas = info['content_ratings']['results'] - us_cert = utils.dictfind(mpaas, "iso_3166_1", "US") - if us_cert: - mpaa = us_cert["rating"] - elif mpaas: - mpaa = mpaas[0]['rating'] - else: - mpaa = "" - tvshow = VideoItem(label=info.get('name'), - path=PLUGIN_BASE + 'extendedtvinfo&&id=%s' % tmdb_id) - tvshow.set_infos({'title': info.get('name'), - 'originaltitle': info.get('original_name', ""), - 'duration': duration, - 'mpaa': mpaa, - 'genre': " / ".join([i["name"] for i in info["genres"]]), - 'plot': info.get("overview"), - 'year': utils.get_year(info.get('first_air_date')), - 'mediatype': "tvshow", - 'rating': round(info['vote_average'], 1) if info.get('vote_average') else "", - 'country': info.get('original_language'), - 'userrating': info.get('rating'), - 'votes': info.get('vote_count'), - 'premiered': info.get('first_air_date'), - 'Status': translate_status(info.get('status'))}) - tvshow.set_properties({'credit_id': info.get('credit_id'), - 'id': tmdb_id, - 'popularity': round(info['popularity'], 1) if info.get('popularity') else "", - 'showtype': info.get('type'), - 'homepage': info.get('homepage'), - 'last_air_date': info.get('last_air_date'), - 'totalepisodes': info.get('number_of_episodes'), - 'totalseasons': info.get('number_of_seasons'), - 'in_production': info.get('in_production')}) - tvshow.set_artwork(get_image_urls(poster=info.get("poster_path"), - fanart=info.get("backdrop_path"))) - if dbid: - local_item = local_db.get_tvshow(dbid) - tvshow.update_from_listitem(local_item) - else: - tvshow = local_db.merge_with_local("tvshow", [tvshow])[0] - # hack to get tmdb rating instead of local one - tvshow.set_info("rating", round( - info['vote_average'], 1) if info.get('vote_average') else "") - certifications = merge_with_cert_desc( - handle_content_ratings(info["content_ratings"]["results"]), "tv") - listitems = {"actors": handle_people(info["credits"]["cast"]), - "similar": handle_tvshows(info["similar"]["results"]), - "studios": handle_companies(info["production_companies"]), - "networks": handle_companies(info["networks"]), - "certifications": certifications, - "crew": handle_people(info["credits"]["crew"]), - "genres": handle_text(info["genres"]), - "keywords": handle_text(info["keywords"]["results"]), - "videos": videos, - "seasons": handle_seasons(info["seasons"]), - "images": handle_images(info["images"]["posters"]), - "backdrops": handle_images(info["images"]["backdrops"])} - return (tvshow, listitems, account_states) - - -def extended_season_info(tvshow_id, season_number): - ''' - get listitem with extended info for season (*tvshow_id, *season_number) - ''' - if not tvshow_id or season_number is None: - return None - tvshow = get_tvshow(tvshow_id) - params = {"append_to_response": ALL_SEASON_PROPS, - "language": addon.setting("LanguageID"), - "include_image_language": "en,null,%s" % addon.setting("LanguageID")} - response = get_data(url="tv/%s/season/%s" % (tvshow_id, season_number), - params=params, - cache_days=7) - if not response: - utils.notify("Could not find season info") - return None - if response.get("name"): - title = response["name"] - elif season_number == "0": - title = addon.LANG(20381) - else: - title = "%s %s" % (addon.LANG(20373), season_number) - season = VideoItem(label=title) - season.set_infos({'plot': response["overview"], - 'tvshowtitle': tvshow.get('name'), - 'title': title, - 'premiered': response["air_date"]}) - season.set_artwork(get_image_urls(poster=response.get("poster_path"))) - season.set_properties({'id': response["id"]}) - videos = handle_videos( - response["videos"]["results"]) if "videos" in response else [] - listitems = {"actors": handle_people(response["credits"]["cast"]), - "crew": handle_people(response["credits"]["crew"]), - "videos": videos, - "episodes": handle_episodes(response["episodes"]), - "images": handle_images(response["images"]["posters"]), - "backdrops": handle_images(response["images"].get("backdrops", []))} - return (season, listitems) - - -def get_episode(tvshow_id, season, episode, cache_days=7): - if not tvshow_id or not episode: - return None - if not season: - season = 0 - params = {"append_to_response": ALL_EPISODE_PROPS, - "language": addon.setting("LanguageID"), - "include_image_language": "en,null,%s" % addon.setting("LanguageID")} - if Login.check_login(): - params["session_id"] = Login.get_session_id() - return get_data(url="tv/%s/season/%s/episode/%s" % (tvshow_id, season, episode), - params=params, - cache_days=cache_days) - - -def extended_episode_info(tvshow_id, season, episode, cache_days=7): - ''' - get ListItem and lists with extended info for episode (*tvshow_id, *season, *episode) - ''' - response = get_episode(tvshow_id, season, episode, cache_days) - if not response: - utils.notify("Could not find episode info") - return None - answer = {"actors": handle_people(response["credits"]["cast"] + response["credits"]["guest_stars"]), - "crew": handle_people(response["credits"]["crew"]), - "videos": handle_videos(response["videos"]["results"]) if "videos" in response else [], - "images": handle_images(response["images"]["stills"])} - return (handle_episodes([response])[0], answer, response.get("account_states")) - - -def extended_actor_info(actor_id) -> Tuple[VideoItem, dict]: - """gets ListItem and lists with extended info for actor with actor_id - data is a dict from JSON returned by tmdb for an actor - lists is a dict of "append_to_response" queries extracted from data - info is a Kodi video listitem instance with properties set from data - - Args: - actor_id (str): the tmdb actor id - - Returns: - VideoItem: a populated Kodi listitem - dict: the lists of ListItems items for which actor has role and actor images - None: if no results from tmdb - """ - if not actor_id: - return None - data = get_data(url="person/%s" % (actor_id), - params={"append_to_response": ALL_ACTOR_PROPS}, - cache_days=1) - if not data: - utils.notify("Could not find actor info") - return None - lists = {"movie_roles": handle_movies(data["movie_credits"]["cast"]).reduce("character"), - "tvshow_roles": handle_tvshows(data["tv_credits"]["cast"]).reduce("character"), - "movie_crew_roles": handle_movies(data["movie_credits"]["crew"]).reduce(), - "tvshow_crew_roles": handle_tvshows(data["tv_credits"]["crew"]).reduce(), - "tagged_images": handle_images(data["tagged_images"]["results"]) if "tagged_images" in data else [], - "images": handle_images(data["images"]["profiles"])} - info = VideoItem(label=data['name'], - path="%sextendedactorinfo&&id=%s" % ( - PLUGIN_BASE, data['id']), - infos={'mediatype': "artist"}) - info.set_properties({'adult': data.get('adult'), - 'alsoknownas': " / ".join(data.get('also_known_as', [])), - 'biography': data.get('biography'), - 'birthday': data.get('birthday'), - 'age': utils.calculate_age(data.get('birthday'), data.get('deathday')), - 'character': data.get('character'), #cast - 'department': data.get('department'), #crew - 'job': data.get('job'), #crew - 'id': data['id'], - 'gender': GENDERS.get(data['gender']), - 'cast_id': data.get('cast_id'), - 'credit_id': data.get('credit_id'), #cast and crew - 'deathday': data.get('deathday'), - 'placeofbirth': data.get('place_of_birth'), - 'homepage': data.get('homepage'), - "DBMovies": len([d for d in lists["movie_roles"] if "dbid" in d])}) - info.set_artwork(get_image_urls(profile=data.get("profile_path"))) - return (info, lists) - - -def translate_status(status): - ''' - get movies from person with *person_id - ''' - return STATUS.get(status.lower(), status) - - -def get_movie_lists(movie_id): - data = get_movie(movie_id) - return handle_lists(data["lists"]["results"]) - - -def get_rated_media_items(media_type, sort_by=None, page=1, cache_days=0): - ''' - takes "tv/episodes", "tv" or "movies" - ''' - if Login.check_login(): - session_id = Login.get_session_id() - account_id = Login.get_account_id() - if not session_id: - utils.notify("Could not get session id") - return [] - params = {"sort_by": sort_by, - "page": page, - "session_id": session_id, - "language": addon.setting("LanguageID")} - data = get_data(url="account/%s/rated/%s" % (account_id, media_type), - params=params, - cache_days=cache_days) - else: - session_id = Login.get_guest_session_id() - if not session_id: - utils.notify("Could not get session id") - return [] - params = {"language": addon.setting("LanguageID"), - "page": page} - data = get_data(url="guest_session/%s/rated/%s" % (session_id, media_type), - params=params, - cache_days=0) - if media_type == "tv/episodes": - itemlist = handle_episodes(data["results"]) - elif media_type == "tv": - itemlist = handle_tvshows(data["results"], False, None) - else: - itemlist = handle_movies(data["results"], False, None) - itemlist.set_totals(data["total_results"]) - itemlist.set_total_pages(data["total_pages"]) - return itemlist - - -def get_fav_items(media_type, sort_by=None, page=1): - ''' - takes "tv/episodes", "tv" or "movies" - ''' - session_id = Login.get_session_id() - account_id = Login.get_account_id() - if not session_id: - utils.notify("Could not get session id") - return [] - params = {"sort_by": sort_by, - "language": addon.setting("LanguageID"), - "page": page, - "session_id": session_id} - data = get_data(url="account/%s/favorite/%s" % (account_id, media_type), - params=params, - cache_days=0) - if "results" not in data: - return [] - if media_type == "tv": - itemlist = handle_tvshows(data["results"], False, None) - elif media_type == "tv/episodes": - itemlist = handle_episodes(data["results"]) - else: - itemlist = handle_movies(data["results"], False, None) - itemlist.set_totals(data["total_results"]) - itemlist.set_total_pages(data["total_pages"]) - return itemlist - - -def get_movies_from_list(list_id, cache_days=5): - ''' - get movie dict list from tmdb list. - ''' - data = get_data(url="list/%s" % (list_id), - params={"language": addon.setting("LanguageID")}, - cache_days=cache_days) - return handle_movies(data["items"], False, None) if data else [] - - -def get_popular_actors(): - ''' - get dict list containing popular actors / directors / writers - ''' - response = get_data(url="person/popular", - cache_days=1) - return handle_people(response["results"]) - - -def get_actor_credits(actor_id, media_type): - ''' - media_type: movie or tv - ''' - response = get_data(url="person/%s/%s_credits" % (actor_id, media_type), - cache_days=1) - return handle_movies(response["cast"]) - - -def get_movie(movie_id, light=False, cache_days=30) -> Union[dict, None]: - """gets details from tmdb for a moview with tmdb movie-id - - Args: - movie_id (str): tmdb movie id - light (bool, optional): return limited info. Defaults to False. - cache_days (int, optional):days to use cache vice new query. - Defaults to 30. - - Returns: - Union[dict, None]: A dict of movie infos. If no response from TMDB - returns None - """ - params = {"include_image_language": "en,null,%s" % addon.setting("LanguageID"), - "language": addon.setting("LanguageID"), - "append_to_response": None if light else ALL_MOVIE_PROPS - } - if Login.check_login(): - params["session_id"] = Login.get_session_id() - return get_data(url="movie/%s" % (movie_id), - params=params, - cache_days=cache_days) - - -def get_similar_movies(movie_id): - ''' - get dict list containing movies similar to *movie_id - ''' - response = get_movie(movie_id) - if not response or not response.get("similar"): - return [] - return handle_movies(response["similar"]["results"]) - - -def get_similar_tvshows(tvshow_id): - ''' - return list with similar tvshows for show with *tvshow_id (TMDB ID) - ''' - params = {"append_to_response": ALL_TV_PROPS, - "language": addon.setting("LanguageID"), - "include_image_language": "en,null,%s" % addon.setting("LanguageID")} - if Login.check_login(): - params["session_id"] = Login.get_session_id() - response = get_data(url="tv/%s" % (tvshow_id), - params=params, - cache_days=10) - if not response.get("similar"): - return [] - return handle_tvshows(response["similar"]["results"]) - - -def get_tvshows(tvshow_type): - ''' - return list with tv shows - available types: airing, on_the_air, top_rated, popular - ''' - response = get_data(url="tv/%s" % (tvshow_type), - params={"language": addon.setting("LanguageID")}, - cache_days=0.3) - if not response.get("results"): - return [] - return handle_tvshows(response["results"], False, None) - - -def get_movies(movie_type: str) -> Union[list, dict]: - """gets list with movies of movie_type from tmdb - - Args: - movie_type (str): now_playing, upcoming, top_rated, popular - - Returns: - list: [description] - """ - response = get_data(url="movie/%s" % (movie_type), - params={"language": addon.setting("LanguageID")}, - cache_days=0.3) - if not response.get("results"): - return [] - return handle_movies(response["results"], False, None) - - -def get_set_movies(set_id): - ''' - return list with movies which are part of set with *set_id - ''' - params = {"append_to_response": "images", - "language": addon.setting("LanguageID"), - "include_image_language": "en,null,%s" % addon.setting("LanguageID")} - response = get_data(url="collection/%s" % (set_id), - params=params, - cache_days=14) - if not response: - return [], {} - artwork = get_image_urls(poster=response.get("poster_path"), - fanart=response.get("backdrop_path")) - info = {"label": response["name"], - "overview": response["overview"], - "id": response["id"]} - info.update(artwork) - return handle_movies(response.get("parts", [])), info - - -def get_person_movies(person_id): - ''' - get movies from person with *person_id - ''' - response = get_data(url="person/%s/credits" % (person_id), - params={"language": addon.setting("LanguageID")}, - cache_days=14) - # return handle_movies(response["crew"]) + handle_movies(response["cast"]) - if not response or "crew" not in response: - return [] - return handle_movies(response["crew"]) - - -def sort_lists(lists): - if not Login.check_login(): - return lists - ids = [i["id"] for i in get_account_lists(10)] - own_lists = [i for i in lists if i.get_property("id") in ids] - for item in own_lists: - item.set_property("account", "True") - misc_lists = [i for i in lists if i.get_property("id") not in ids] - return own_lists + misc_lists - - -def search_media(media_name=None, year='', media_type="movie", cache_days=1): - ''' - return list of items with type *media_type for search with *media_name - ''' - if not media_name: - return None - params = {"query": "{} {}".format(media_name, year) if year else media_name, - "language": addon.setting("language"), - "include_adult": addon.setting("include_adults").lower()} - response = get_data(url="search/%s" % (media_type), - params=params, - cache_days=cache_days) - if response == "Empty": - return None - for item in response['results']: - if item['id']: - return item['id'] - - -Login = LoginProvider(username=addon.setting("tmdb_username"), - password=addon.setting("tmdb_password")) diff --git a/script.extendedinfo/resources/lib/Trakt.py b/script.extendedinfo/resources/lib/Trakt.py deleted file mode 100644 index 8b78d724c..000000000 --- a/script.extendedinfo/resources/lib/Trakt.py +++ /dev/null @@ -1,208 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import datetime -import urllib.error -import urllib.parse -import urllib.request - -from kutils import ItemList, VideoItem, addon, local_db, utils -from resources.lib import TheMovieDB as tmdb - -TRAKT_KEY = 'e9a7fba3fa1b527c08c073770869c258804124c5d7c984ce77206e695fbaddd5' -BASE_URL = "https://api-v2launch.trakt.tv/" -HEADERS = { - 'Content-Type': 'application/json', - 'trakt-api-key': TRAKT_KEY, - 'trakt-api-version': 2 -} -PLUGIN_BASE = "plugin://script.extendedinfo/?info=" - - -def get_episodes(content): - shows = ItemList(content_type="episodes") - url = "" - if content == "shows": - url = 'calendars/shows/%s/14' % datetime.date.today() - elif content == "premieres": - url = 'calendars/shows/premieres/%s/14' % datetime.date.today() - results = get_data(url=url, - params={"extended": "full"}, - cache_days=0.3) - count = 1 - if not results: - return None - for day in results.items(): - for episode in day[1]: - ep = episode["episode"] - tv = episode["show"] - title = ep["title"] if ep["title"] else "" - label = "{0} - {1}x{2}. {3}".format(tv["title"], - ep["season"], - ep["number"], - title) - show = VideoItem(label=label, - path=PLUGIN_BASE + 'extendedtvinfo&&tvdb_id=%s' % tv["ids"]["tvdb"]) - show.set_infos({'title': title, - 'aired': ep["first_aired"], - 'season': ep["season"], - 'episode': ep["number"], - 'tvshowtitle': tv["title"], - 'mediatype': "episode", - 'year': tv.get("year"), - 'duration': tv["runtime"] * 60 if tv["runtime"] else "", - 'studio': tv["network"], - 'plot': tv["overview"], - 'country': tv["country"], - 'status': tv["status"], - 'trailer': tv["trailer"], - 'imdbnumber': ep["ids"]["imdb"], - 'rating': tv["rating"], - 'genre': " / ".join(tv["genres"]), - 'mpaa': tv["certification"]}) - show.set_properties({'tvdb_id': ep["ids"]["tvdb"], - 'id': ep["ids"]["tvdb"], - 'imdb_id': ep["ids"]["imdb"], - 'homepage': tv["homepage"]}) - if tv["ids"].get("tmdb"): - art_info = tmdb.get_tvshow(tv["ids"]["tmdb"], light=True) - show.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path"), - fanart=art_info.get("backdrop_path"))) - shows.append(show) - count += 1 - if count > 20: - break - return shows - - -def handle_movies(results): - movies = ItemList(content_type="movies") - path = 'extendedinfo&&id=%s' if addon.bool_setting( - "infodialog_onclick") else "playtrailer&&id=%s" - for i in results: - item = i["movie"] if "movie" in i else i - trailer = "%syoutubevideo&&id=%s" % ( - PLUGIN_BASE, utils.extract_youtube_id(item["trailer"])) - movie = VideoItem(label=item["title"], - path=PLUGIN_BASE + path % item["ids"]["tmdb"]) - movie.set_infos({'title': item["title"], - 'duration': item["runtime"] * 60 if item["runtime"] else "", - 'tagline': item["tagline"], - 'mediatype': "movie", - 'trailer': trailer, - 'year': item["year"], - 'mpaa': item["certification"], - 'plot': item["overview"], - 'imdbnumber': item["ids"]["imdb"], - 'premiered': item["released"], - 'rating': round(item["rating"], 1), - 'votes': item["votes"], - 'genre': " / ".join(item["genres"])}) - movie.set_properties({'id': item["ids"]["tmdb"], - 'imdb_id': item["ids"]["imdb"], - 'trakt_id': item["ids"]["trakt"], - 'watchers': item.get("watchers"), - 'language': item.get("language"), - 'homepage': item.get("homepage")}) - art_info = tmdb.get_movie(item["ids"]["tmdb"], light=True) - movie.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path"), - fanart=art_info.get("backdrop_path"))) - movies.append(movie) - movies = local_db.merge_with_local(media_type="movie", - items=movies, - library_first=False) - movies.set_sorts(["mpaa", "duration"]) - return movies - - -def handle_tvshows(results): - shows = ItemList(content_type="tvshows") - for i in results: - item = i["show"] if "show" in i else i - airs = item.get("airs", {}) - show = VideoItem(label=item["title"], - path='%sextendedtvinfo&&tvdb_id=%s' % (PLUGIN_BASE, item['ids']["tvdb"])) - show.set_infos({'mediatype': "tvshow", - 'title': item["title"], - 'duration': item["runtime"] * 60 if item["runtime"] else "", - 'year': item["year"], - 'premiered': item["first_aired"][:10], - 'country': item["country"], - 'rating': round(item["rating"], 1), - 'votes': item["votes"], - 'imdbnumber': item['ids']["imdb"], - 'mpaa': item["certification"], - 'trailer': item["trailer"], - 'status': item.get("status"), - 'studio': item["network"], - 'genre': " / ".join(item["genres"]), - 'plot': item["overview"]}) - show.set_properties({'id': item['ids']["tmdb"], - 'tvdb_id': item['ids']["tvdb"], - 'imdb_id': item['ids']["imdb"], - 'trakt_id': item['ids']["trakt"], - 'language': item["language"], - 'aired_episodes': item["aired_episodes"], - 'homepage': item["homepage"], - 'airday': airs.get("day"), - 'airshorttime': airs.get("time"), - 'watchers': item.get("watchers")}) - art_info = tmdb.get_tvshow(item["ids"]["tmdb"], light=True) - show.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path"), - fanart=art_info.get("backdrop_path"))) - shows.append(show) - shows = local_db.merge_with_local(media_type="tvshow", - items=shows, - library_first=False) - shows.set_sorts(["mpaa", "duration"]) - return shows - - -def get_shows(show_type): - results = get_data(url='shows/%s' % show_type, - params={"extended": "full"}) - return handle_tvshows(results) if results else [] - - -def get_shows_from_time(show_type, period="monthly"): - results = get_data(url='shows/%s/%s' % (show_type, period), - params={"extended": "full"}) - return handle_tvshows(results) if results else [] - - -def get_movies(movie_type): - results = get_data(url='movies/%s' % movie_type, - params={"extended": "full"}) - return handle_movies(results) if results else [] - - -def get_movies_from_time(movie_type, period="monthly"): - results = get_data(url='movies/%s/%s' % (movie_type, period), - params={"extended": "full"}) - return handle_movies(results) if results else [] - - -def get_similar(media_type, imdb_id): - if not imdb_id or not media_type: - return None - results = get_data(url='%ss/%s/related' % (media_type, imdb_id), - params={"extended": "full"}) - if not results: - return None - if media_type == "show": - return handle_tvshows(results) - elif media_type == "movie": - return handle_movies(results) - - -def get_data(url, params=None, cache_days=10): - params = params if params else {} - params["limit"] = 10 - url = "%s%s?%s" % (BASE_URL, url, urllib.parse.urlencode(params)) - return utils.get_JSON_response(url=url, - folder="Trakt", - headers=HEADERS, - cache_days=cache_days) diff --git a/script.extendedinfo/resources/lib/WindowManager.py b/script.extendedinfo/resources/lib/WindowManager.py deleted file mode 100644 index 05ac58459..000000000 --- a/script.extendedinfo/resources/lib/WindowManager.py +++ /dev/null @@ -1,280 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import os -import re -from typing import Optional - -import xbmc -import xbmcgui -import xbmcvfs - -from kutils import addon, busy, local_db, player, utils, windows - -from . import TheMovieDB as tmdb - -INFO_XML_CLASSIC = 'script-%s-DialogVideoInfo.xml' % (addon.ID) -LIST_XML_CLASSIC = 'script-%s-VideoList.xml' % (addon.ID) -ACTOR_XML_CLASSIC = 'script-%s-DialogInfo.xml' % (addon.ID) -if addon.bool_setting("force_native_layout") and addon.setting("xml_version") != addon.VERSION: - addon.set_setting("xml_version", addon.VERSION) - INFO_XML = 'script-%s-DialogVideoInfo-classic.xml' % (addon.ID) - LIST_XML = 'script-%s-VideoList-classic.xml' % (addon.ID) - ACTOR_XML = 'script-%s-DialogInfo-classic.xml' % (addon.ID) - path = os.path.join(addon.PATH, "resources", "skins", "Default", "1080i") - xbmcvfs.copy(strSource=os.path.join(path, INFO_XML_CLASSIC), - strDestination=os.path.join(path, INFO_XML)) - xbmcvfs.copy(strSource=os.path.join(path, LIST_XML_CLASSIC), - strDestination=os.path.join(path, LIST_XML)) - xbmcvfs.copy(strSource=os.path.join(path, ACTOR_XML_CLASSIC), - strDestination=os.path.join(path, ACTOR_XML)) -else: - INFO_XML = INFO_XML_CLASSIC - LIST_XML = LIST_XML_CLASSIC - ACTOR_XML = ACTOR_XML_CLASSIC - - -class WindowManager: - window_stack = [] - - def __init__(self): - self.active_dialog = None - self.saved_background = addon.get_global("infobackground") - self.saved_control = xbmc.getInfoLabel("System.CurrentControlId") - self.saved_dialogstate = xbmc.getCondVisibility( - "Window.IsActive(Movieinformation)") - # self.monitor = SettingsMonitor() - self.monitor = xbmc.Monitor() - - def open_movie_info(self, movie_id=None, dbid=None, name=None, imdb_id=None): - """ - opens movie info dialog, deal with window stack - """ - busy.show_busy() - from .dialogs.DialogMovieInfo import DialogMovieInfo - dbid = int(dbid) if dbid and int(dbid) > 0 else None - if not movie_id: - movie_id = tmdb.get_movie_tmdb_id(imdb_id=imdb_id, - dbid=dbid, - name=name) - dialog = DialogMovieInfo(INFO_XML, - addon.PATH, - id=movie_id, - dbid=dbid) - busy.hide_busy() - self.open_infodialog(dialog) - - def open_tvshow_info(self, tmdb_id=None, dbid=None, tvdb_id=None, imdb_id=None, name=None): - """ - open tvshow info, deal with window stack - """ - busy.show_busy() - dbid = int(dbid) if dbid and int(dbid) > 0 else None - from .dialogs.DialogTVShowInfo import DialogTVShowInfo - if tmdb_id: - pass - elif tvdb_id: - tmdb_id = tmdb.get_show_tmdb_id(tvdb_id) - elif imdb_id: - tmdb_id = tmdb.get_show_tmdb_id(tvdb_id=imdb_id, - source="imdb_id") - elif dbid: - tvdb_id = local_db.get_imdb_id(media_type="tvshow", - dbid=dbid) - if tvdb_id: - tmdb_id = tmdb.get_show_tmdb_id(tvdb_id) - elif name: - tmdb_id = tmdb.search_media(media_name=name, - year="", - media_type="tv") - dialog = DialogTVShowInfo(INFO_XML, - addon.PATH, - tmdb_id=tmdb_id, - dbid=dbid) - busy.hide_busy() - self.open_infodialog(dialog) - - def open_season_info(self, tvshow_id=None, season: Optional[int] = None, tvshow=None, dbid=None): - """ - open season info, deal with window stack - needs *season AND (*tvshow_id OR *tvshow) - """ - busy.show_busy() - from .dialogs.DialogSeasonInfo import DialogSeasonInfo - if not tvshow_id: - params = {"query": tvshow, - "language": addon.setting("language")} - response = tmdb.get_data(url="search/tv", - params=params, - cache_days=30) - if response["results"]: - tvshow_id = str(response['results'][0]['id']) - else: - params = {"query": re.sub(r'\(.*?\)', '', tvshow), - "language": addon.setting("language")} - response = tmdb.get_data(url="search/tv", - params=params, - cache_days=30) - if response["results"]: - tvshow_id = str(response['results'][0]['id']) - - dialog = DialogSeasonInfo(INFO_XML, - addon.PATH, - id=tvshow_id, - season=max(0, season), - dbid=int(dbid) if dbid and int(dbid) > 0 else None) - busy.hide_busy() - self.open_infodialog(dialog) - - def open_episode_info(self, tvshow_id=None, season=None, episode=None, tvshow=None, dbid=None): - """ - open season info, deal with window stack - needs (*tvshow_id OR *tvshow) AND *season AND *episode - """ - from .dialogs.DialogEpisodeInfo import DialogEpisodeInfo - if not tvshow_id and tvshow: - tvshow_id = tmdb.search_media(media_name=tvshow, - media_type="tv", - cache_days=7) - dialog = DialogEpisodeInfo(INFO_XML, - addon.PATH, - tvshow_id=tvshow_id, - season=max(0, season), - episode=episode, - dbid=int(dbid) if dbid and int(dbid) > 0 else None) - self.open_infodialog(dialog) - - def open_actor_info(self, actor_id: str=None, name: str=None): - """opens info dialog window for an actor, deals with window stack - A new dialog instance of DialogActorInfo is created and the xml window - (DialogInfo.xml) is opened - - Args: - actor_id (str, optional): tmdb actor id. Defaults to None. - name (str, optional): a string of name or name[ separator name]. - if name is a multiple a select dialog is presented to user to - get a single actor. If name is provided, attempts to get a tmdb - for it. Defaults to None. - - Returns: - None: if no tmdb actor id could be found - """ - from .dialogs.DialogActorInfo import DialogActorInfo - if not actor_id: - name = name.split(" %s " % addon.LANG(20347)) - names = name[0].strip().split(" / ") - if len(names) > 1: - ret = xbmcgui.Dialog().select(heading=addon.LANG(32027), - list=names) - if ret == -1: - return None - name = names[ret] - else: - name = names[0] - busy.show_busy() - actor_info = tmdb.get_person_info(name) - if not actor_info: - return None - actor_id = actor_info["id"] - else: - busy.show_busy() - dialog = DialogActorInfo(ACTOR_XML, - addon.PATH, - id=actor_id) - busy.hide_busy() - self.open_infodialog(dialog) - - def open_video_list(self, listitems=None, filters=None, mode="filter", list_id=False, - filter_label="", force=False, media_type="movie", search_str=""): - """opens video list deals with window stack items - - Args: - listitems (dict, optional): [description]. Defaults to None. - filters ([type], optional): [description]. Defaults to None. - mode (str, optional): [description]. Defaults to "filter". - list_id (bool, optional): [description]. Defaults to False. - filter_label (str, optional): [description]. Defaults to "". - force (bool, optional): [description]. Defaults to False. - media_type (str, optional): [description]. Defaults to "movie". - search_str (str, optional): [description]. Defaults to "". - """ - # utils.log('wm open_video_list listitems: {}'.format(listitems)) #debug - from .dialogs import DialogVideoList - Browser = DialogVideoList.get_window(windows.DialogXML) - dialog = Browser(LIST_XML, - addon.PATH, - listitems=listitems, - filters=[] if not filters else filters, - mode=mode, - list_id=list_id, - force=force, - filter_label=filter_label, - search_str=search_str, - type=media_type) - self.open_dialog(dialog) - - def open_youtube_list(self, search_str="", filters=None, filter_label="", media_type="video"): - """ - open video list, deal with window stack - """ - from .dialogs import DialogYoutubeList - YouTube = DialogYoutubeList.get_window(windows.DialogXML) - dialog = YouTube('script-%s-YoutubeList.xml' % addon.ID, addon.PATH, - search_str=search_str, - filters=[] if not filters else filters, - type=media_type) - self.open_dialog(dialog) - - def open_infodialog(self, dialog): - if dialog.info: - self.open_dialog(dialog) - else: - self.active_dialog = None - utils.notify(addon.LANG(32143)) - - def open_dialog(self, dialog): - if self.active_dialog: - self.window_stack.append(self.active_dialog) - self.active_dialog.close() - utils.check_version() - if not addon.setting("first_start_infodialog"): - addon.set_setting("first_start_infodialog", "True") - xbmcgui.Dialog().ok(heading=addon.NAME, - message=addon.LANG(32140) + '[CR]' + addon.LANG(32141)) - self.active_dialog = dialog - try: - dialog.doModal() - except SystemExit: - pass -# if dialog.canceled: -# addon.set_global("infobackground", self.saved_background) -# self.window_stack = [] -# return None - if self.window_stack and not self.monitor.abortRequested(): - self.active_dialog = self.window_stack.pop() - xbmc.sleep(300) - try: - self.active_dialog.doModal() - except SystemExit: - pass - else: - addon.set_global("infobackground", self.saved_background) - - def play_youtube_video(self, youtube_id="", listitem=None): - """ - play youtube vid with info from *listitem - """ - if self.active_dialog and self.active_dialog.window_type == "dialog": - self.active_dialog.close() - xbmc.executebuiltin("Dialog.Close(movieinformation)") - xbmc.executebuiltin("RunPlugin(plugin://plugin.video.youtube/play/?video_id=" + - youtube_id + "&screensaver=true&incognito=true)") - if self.active_dialog and self.active_dialog.window_type == "dialog": - player.wait_for_video_end() - self.active_dialog.doModal() - - -wm = WindowManager() diff --git a/script.extendedinfo/resources/lib/bandsintown.py b/script.extendedinfo/resources/lib/bandsintown.py new file mode 100644 index 000000000..336760988 --- /dev/null +++ b/script.extendedinfo/resources/lib/bandsintown.py @@ -0,0 +1,67 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details +"""Obtains local events from BandsInTown + +The current API TOU does not permit getting an API key for this addon. This +module is inop, but retained in case there is a change in the future. + +""" + +import urllib.error +import urllib.parse +import urllib.request + +from resources.kutil131 import ItemList + +from resources.kutil131 import VideoItem, utils + +# TVRAGE_KEY = 'VBp9BuIr5iOiBeWCFRMG' +API_KEY = '' +BASE_URL = f"http://api.bandsintown.com/events/search?format=json&api_version=2.0&app_id={API_KEY}&" + + +def handle_events(results: list) -> ItemList: + """converts a list of BandsinTown events to a kutils131 ItemList + + Args: + results (list): list of event dicts + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + events = ItemList() + for event in results: + venue = event['venue'] + item = VideoItem(label=venue['name']) + item.set_properties({'date': event['datetime'].replace("T", " - ").replace(":00", "", 1), + 'city': venue['city'], + 'lat': venue['latitude'], + 'lon': venue['longitude'], + 'id': venue['id'], + 'url': venue['url'], + 'region': venue['region'], + 'country': venue['country'], + 'artists': " / ".join([art for art in event["artists"]])}) + events.append(item) + return events + + +def get_near_events(artists: str) -> ItemList: # not possible with api 2.0 + """Queries BandsInTown for events + + Args: + artists (str): _description_ + + Returns: + ItemList: A kutils131 ItemList of VideoItems for artist events + """ + arts = [urllib.parse.quote(art['artist'].encode("utf-8")) + for art in artists[:50]] + artist_str = 'artists[]=' + '&artists[]='.join(arts) + url = BASE_URL + \ + f'location=use_geoip&radius=50&per_page=100&{artist_str}' + results = utils.get_JSON_response(url, folder="BandsInTown") + if results: + return handle_events(results) + return [] diff --git a/script.extendedinfo/resources/lib/dialogs/DialogActorInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogActorInfo.py deleted file mode 100644 index 380750109..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogActorInfo.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmcgui -from kutils import ActionHandler, addon, imagetools -from resources.lib import TheMovieDB as tmdb - -from .DialogBaseInfo import DialogBaseInfo - -ID_CONTROL_PLOT = 132 - -ch = ActionHandler() - - -class DialogActorInfo(DialogBaseInfo): - """creates a dialog window for actor info - - Args: - DialogBaseInfo (_type_): handles common window details - """ - TYPE = "Actor" - LISTS = [(150, "movie_roles"), - (250, "tvshow_roles"), - (450, "images"), - (550, "movie_crew_roles"), - (650, "tvshow_crew_roles"), - (750, "tagged_images")] - - def __init__(self, *args, **kwargs) -> None: - """Constructs the dialog window with actor info from tmdb - slef.info is a ListItem for the actor - self.lists is a list of ListItems for the actor's movies and tv shows - - Arguments: - *args: dialog xml filename - this addon path (for cache) - **kwargs: id[str]: the tmdb actor id - - Returns: - None: if no tmdb extended actor info available - """ - super(DialogActorInfo, self).__init__(*args, **kwargs) - data = tmdb.extended_actor_info(actor_id=kwargs.get('id')) - if not data: - return None - self.info, self.lists = data - self.info.update_properties( - imagetools.blur(self.info.get_art("thumb"))) - - def onInit(self): - self.get_youtube_vids(self.info.label) - super(DialogActorInfo, self).onInit() - - def onClick(self, control_id): - super(DialogActorInfo, self).onClick(control_id) - ch.serve(control_id, self) - - @ch.click(ID_CONTROL_PLOT) - def show_plot(self, control_id): - xbmcgui.Dialog().textviewer(heading=addon.LANG(32037), - text=self.info.get_property("biography")) diff --git a/script.extendedinfo/resources/lib/dialogs/DialogBaseInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogBaseInfo.py deleted file mode 100644 index 469d1d724..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogBaseInfo.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmc -import xbmcgui - -from kutils import (ActionHandler, VideoItem, addon, kodijson, selectdialog, - slideshow, utils, windows, youtube) - -from resources.lib import TheMovieDB as tmdb -from resources.lib.WindowManager import wm - -ch = ActionHandler() - -ID_LIST_YOUTUBE = 350 -ID_LIST_IMAGES = 1250 -ID_BUTTON_BOUNCEUP = 20000 -ID_BUTTON_BOUNCEDOWN = 20001 - - -class DialogBaseInfo(windows.DialogXML): - """Class constructs a basic dialog xml window. Subclasses augment for different - dialog types (eg actor info or movie info) - - Args: - windows.DialogXML (DialogXML): a kutils class derived from xbmcgui.WindowXMLDialog - and kutils WindowMixin classes - - Returns: - _type_: _description_ - """ - ACTION_PREVIOUS_MENU = [92, 9] - ACTION_EXIT_SCRIPT = [13, 10] - - def __init__(self, *args, **kwargs): - super(DialogBaseInfo, self).__init__(*args, **kwargs) - self.logged_in: bool = tmdb.Login.check_login() - self.bouncing = False - self.last_focus = None - self.lists = None - self.states = False - self.yt_listitems = [] - self.info = VideoItem() - self.last_control = None - self.last_position = None - - def onInit(self, *args, **kwargs): - super(DialogBaseInfo, self).onInit() - # self.set_buttons() - self.info.to_windowprops(window_id=self.window_id) #kutils sets home window - #properties from the info listitem - for container_id, key in self.LISTS: - try: - self.getControl(container_id).reset() - items = [i.get_listitem() for i in self.lists[key]] - self.getControl(container_id).addItems(items) - except Exception as err: - utils.log(f'Notice: No container with id {container_id} available and {err}') - if self.last_control: - self.setFocusId(self.last_control) - if self.last_control and self.last_position: - try: - self.getControl(self.last_control).selectItem( - self.last_position) - except Exception: - pass - addon.set_global("ImageColor", self.info.get_property('ImageColor')) - addon.set_global("infobackground", self.info.get_art('fanart_small')) - self.setProperty("type", self.TYPE) - self.setProperty("tmdb_logged_in", "true" if self.logged_in else "") - - def onAction(self, action): - ch.serve_action(action, self.getFocusId(), self) - - def onClick(self, control_id): - super(DialogBaseInfo, self).onClick(control_id) - ch.serve(control_id, self) - - def onFocus(self, control_id): - if control_id == ID_BUTTON_BOUNCEUP: - if not self.bouncing: - self.bounce("up") - self.setFocusId(self.last_focus) - elif control_id == ID_BUTTON_BOUNCEDOWN: - if not self.bouncing: - self.bounce("down") - self.setFocusId(self.last_focus) - self.last_focus = control_id - - def close(self): - try: - self.last_position = self.getFocus().getSelectedPosition() - except Exception: - self.last_position = None - addon.set_global("infobackground", "") - self.last_control = self.getFocusId() - super(DialogBaseInfo, self).close() - - @utils.run_async - def bounce(self, identifier): - self.bouncing = True - self.setProperty("Bounce.%s" % identifier, "true") - xbmc.sleep(200) - self.clearProperty("Bounce.%s" % identifier) - self.bouncing = False - - @ch.click_by_type("music") - # hack: use "music" until "pictures" got added to core - def open_image(self, control_id): - key = [key for container_id, - key in self.LISTS if container_id == control_id][0] - pos = slideshow.open(listitems=self.lists[key], - index=self.getControl(control_id).getSelectedPosition()) - self.getControl(control_id).selectItem(pos) - - @ch.click_by_type("video") - def play_youtube_video(self, control_id): - wm.play_youtube_video(youtube_id=self.FocusedItem(control_id).getProperty("youtube_id"), - listitem=self.FocusedItem(control_id)) - - @ch.click_by_type("artist") - def open_actor_info(self, control_id): - wm.open_actor_info(actor_id=self.FocusedItem( - control_id).getProperty("id")) - - @ch.click_by_type("movie") - def open_movie_info(self, control_id): - wm.open_movie_info(movie_id=self.FocusedItem(control_id).getProperty("id"), - dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) - - @ch.click_by_type("tvshow") - def open_tvshow_info(self, control_id): - wm.open_tvshow_info(tmdb_id=self.FocusedItem(control_id).getProperty("id"), - dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) - - @ch.click_by_type("episode") - def open_episode_info(self, control_id): - info = self.FocusedItem(control_id).getVideoInfoTag() - wm.open_episode_info(tvshow=self.info.get_info("tvshowtitle"), - tvshow_id=self.tvshow_id, - season=info.getSeason(), - episode=info.getEpisode()) - - @ch.context("music") - def thumbnail_options(self, control_id): - listitem = self.FocusedItem(control_id) - art_type = listitem.getProperty("type") - options = [] - if self.info.get_info("dbid") and art_type == "poster": - options.append(("db_art", addon.LANG(32006))) - if self.info.get_info("dbid") and art_type == "fanart": - options.append(("db_art", addon.LANG(32007))) - movie_id = listitem.getProperty("movie_id") - if movie_id: - options.append(("movie_info", addon.LANG(10524))) - if not options: - return None - action = utils.contextmenu(options=options) - if action == "db_art": - kodijson.set_art(media_type=self.getProperty("type"), - art={art_type: listitem.get_art("original")}, - dbid=self.info.get_info("dbid")) - elif action == "movie_info": - wm.open_movie_info(movie_id=listitem.getProperty("movie_id"), - dbid=listitem.getVideoInfoTag().getDbId()) - - @ch.context("video") - def video_context_menu(self, control_id): - index = xbmcgui.Dialog().contextmenu(list=[addon.LANG(33003)]) - if index == 0: - utils.download_video(self.FocusedItem( - control_id).getProperty("youtube_id")) - - @ch.context("movie") - def movie_context_menu(self, control_id): - movie_id = self.FocusedItem(control_id).getProperty("id") - dbid = self.FocusedItem(control_id).getVideoInfoTag().getDbId() - options = [addon.LANG(32113)] - if self.logged_in: - options.append(addon.LANG(32083)) - index = xbmcgui.Dialog().contextmenu(list=options) - if index == 0: - rating = utils.input_userrating() - if rating == -1: - return None - tmdb.set_rating(media_type="movie", - media_id=movie_id, - rating=rating, - dbid=dbid) - xbmc.sleep(2000) - tmdb.get_movie(movie_id=movie_id, - cache_days=0) - elif index == 1: - account_lists = tmdb.get_account_lists() - if not account_lists: - return False - listitems = ["%s (%i)" % (i["name"], i["item_count"]) - for i in account_lists] - i = xbmcgui.Dialog().select(addon.LANG(32136), listitems) - if i > -1: - tmdb.change_list_status(list_id=account_lists[i]["id"], - movie_id=movie_id, - status=True) - - @ch.context("artist") - def person_context_menu(self, control_id): - listitem = self.FocusedItem(control_id) - options = [addon.LANG(32009), addon.LANG(32070)] - credit_id = listitem.getProperty("credit_id") - if credit_id and self.TYPE == "TVShow": - options.append(addon.LANG(32147)) - index = xbmcgui.Dialog().contextmenu(list=options) - if index == 0: - wm.open_actor_info(actor_id=listitem.getProperty("id")) - if index == 1: - filters = [{"id": listitem.getProperty("id"), - "type": "with_people", - "label": listitem.getLabel()}] - wm.open_video_list(filters=filters) - if index == 2: - self.open_credit_dialog(credit_id) - - @ch.context("tvshow") - def tvshow_context_menu(self, control_id): - tvshow_id = self.FocusedItem(control_id).getProperty("id") - dbid = self.FocusedItem(control_id).getVideoInfoTag().getDbId() - credit_id = self.FocusedItem(control_id).getProperty("credit_id") - options = [addon.LANG(32169)] - if credit_id: - options.append(addon.LANG(32147)) - index = xbmcgui.Dialog().contextmenu(list=options) - if index == 0: - rating = utils.input_userrating() - if rating == -1: - return None - tmdb.set_rating(media_type="tvshow", - media_id=tvshow_id, - rating=rating, - dbid=dbid) - xbmc.sleep(2000) - tmdb.get_tvshow(tvshow_id=tvshow_id, - cache_days=0) - if index == 1: - self.open_credit_dialog(credit_id=credit_id) - - @ch.action("parentdir", "*") - @ch.action("parentfolder", "*") - def previous_menu(self, control_id): - onback = self.getProperty("%i_onback" % control_id) - if onback: - xbmc.executebuiltin(onback) - else: - self.close() - - @ch.action("previousmenu", "*") - def exit_script(self, *args): - self.exit() - - @utils.run_async - def get_youtube_vids(self, search_str): - try: - youtube_list = self.getControl(ID_LIST_YOUTUBE) - except Exception: - return None - if not self.yt_listitems: - user_key = addon.setting("Youtube API Key") - self.yt_listitems = youtube.search( - search_str, limit=15, api_key=user_key) - if not self.yt_listitems: - return None - vid_ids = [item.get_property( - "key") for item in self.lists["videos"]] if "videos" in self.lists else [] - youtube_list.reset() - youtube_list.addItems( - [i.get_listitem() for i in self.yt_listitems if i.get_property("youtube_id") not in vid_ids]) - - def open_credit_dialog(self, credit_id): - info = tmdb.get_credit_info(credit_id) - listitems = [] - if "seasons" in info["media"]: - listitems += tmdb.handle_seasons(info["media"]["seasons"]) - if "episodes" in info["media"]: - listitems += tmdb.handle_episodes(info["media"]["episodes"]) - if not listitems: - listitems += [{"label": addon.LANG(19055)}] - index = selectdialog.open(header=addon.LANG(32151), - listitems=listitems) - if index == -1: - return None - listitem = listitems[index] - if listitem["mediatype"] == "episode": - wm.open_episode_info(season=listitem["season"], - episode=listitem["episode"], - tvshow_id=info["media"]["id"]) - elif listitem["mediatype"] == "season": - wm.open_season_info(season=listitem["season"], - tvshow_id=info["media"]["id"]) - - def update_states(self): - if not self.states: - return None - utils.dict_to_windowprops(data=tmdb.get_account_props(self.states), - window_id=self.window_id) diff --git a/script.extendedinfo/resources/lib/dialogs/DialogEpisodeInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogEpisodeInfo.py deleted file mode 100644 index efe60337b..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogEpisodeInfo.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmc - -from kutils import ActionHandler, addon, busy, imagetools -from resources.lib import TheMovieDB as tmdb -from resources.lib.WindowManager import wm - -from .DialogVideoInfo import DialogVideoInfo - -ID_BUTTON_RATED = 6006 - -ch = ActionHandler() - - -class DialogEpisodeInfo(DialogVideoInfo): - TYPE = "Episode" - TYPE_ALT = "episode" - LISTS = [(1000, "actors"), - (750, "crew"), - (1150, "videos"), - (1350, "images")] - - @busy.set_busy - def __init__(self, *args, **kwargs): - super(DialogEpisodeInfo, self).__init__(*args, **kwargs) - self.tvshow_id = kwargs.get('tvshow_id') - tv_info = tmdb.get_tvshow(self.tvshow_id, light=True) - data = tmdb.extended_episode_info(tvshow_id=self.tvshow_id, - season=kwargs.get('season'), - episode=kwargs.get('episode')) - if not data: - return None - self.info, self.lists, self.states = data - self.info.set_info("tvshowtitle", tv_info["name"]) - image_info = imagetools.blur(self.info.get_art("thumb")) - self.info.update_properties(image_info) - - def onInit(self): - super(DialogEpisodeInfo, self).onInit() - search_str = '{} "Season {}" "Episode {}"'.format(self.info.get_info("tvshowtitle"), - self.info.get_info( - 'season'), - self.info.get_info('episode')) - self.get_youtube_vids(search_str) - super(DialogEpisodeInfo, self).update_states() - - def onClick(self, control_id): - super(DialogEpisodeInfo, self).onClick(control_id) - ch.serve(control_id, self) - - @ch.click(ID_BUTTON_RATED) - def open_rating_list(self, control_id): - busy.show_busy() - listitems = tmdb.get_rated_media_items("tv/episodes") - busy.hide_busy() - wm.open_video_list(listitems=listitems) - - def get_identifier(self): - return [self.tvshow_id, - self.info.get_info("season"), - self.info.get_info("episode")] - - def update_states(self): - xbmc.sleep(2000) # delay because MovieDB takes some time to update - info = tmdb.get_episode(tvshow_id=self.tvshow_id, - season=self.info.get_info("season"), - episode=self.info.get_info("episode"), - cache_days=0) - self.states = info.get("account_states") - super(DialogEpisodeInfo, self).update_states() - - def get_manage_options(self): - return [(addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")] diff --git a/script.extendedinfo/resources/lib/dialogs/DialogMovieInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogMovieInfo.py deleted file mode 100644 index a94be6ebf..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogMovieInfo.py +++ /dev/null @@ -1,274 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import threading -from typing import Optional, Union - -import xbmc -import xbmcgui - -from kutils import ActionHandler, addon, busy, imagetools, kodijson, utils -from resources.lib import TheMovieDB as tmdb -from resources.lib import omdb -from resources.lib.WindowManager import wm - -from .DialogVideoInfo import DialogVideoInfo - -ID_LIST_SIMILAR = 150 -ID_LIST_SETS = 250 -ID_LIST_YOUTUBE = 350 -ID_LIST_LISTS = 450 -ID_LIST_STUDIOS = 550 -ID_LIST_CERTS = 650 -ID_LIST_CREW = 750 -ID_LIST_GENRES = 850 -ID_LIST_KEYWORDS = 950 -ID_LIST_ACTORS = 1000 -ID_LIST_REVIEWS = 1050 -ID_LIST_VIDEOS = 1150 -ID_LIST_IMAGES = 1250 -ID_LIST_BACKDROPS = 1350 - -ID_BUTTON_PLAY_NORESUME = 8 -ID_BUTTON_PLAY_RESUME = 9 -ID_BUTTON_TRAILER = 10 -ID_BUTTON_SETRATING = 6001 -ID_BUTTON_OPENLIST = 6002 -ID_BUTTON_ADDTOLIST = 6005 -ID_BUTTON_RATED = 6006 - -ch = ActionHandler() - - -class DialogMovieInfo(DialogVideoInfo): - TYPE = "Movie" - TYPE_ALT = "movie" - LISTS = [(ID_LIST_ACTORS, "actors"), - (ID_LIST_SIMILAR, "similar"), - (ID_LIST_SETS, "sets"), - (ID_LIST_LISTS, "lists"), - (ID_LIST_STUDIOS, "studios"), - (ID_LIST_CERTS, "releases"), - (ID_LIST_CREW, "crew"), - (ID_LIST_GENRES, "genres"), - (ID_LIST_KEYWORDS, "keywords"), - (ID_LIST_REVIEWS, "reviews"), - (ID_LIST_VIDEOS, "videos"), - (ID_LIST_IMAGES, "images"), - (ID_LIST_BACKDROPS, "backdrops")] - - # BUTTONS = [ID_BUTTON_OPENLIST, - # ID_BUTTON_ADDTOLIST] - - def __init__(self, *args, **kwargs): - super(DialogMovieInfo, self).__init__(*args, **kwargs) - data: Optional[dict] = tmdb.extended_movie_info(movie_id=kwargs.get('id'), - dbid=kwargs.get('dbid')) - if not data: - return None - self.info, self.lists, self.states = data - sets_thread = SetItemsThread(self.info.get_property("set_id")) - self.omdb_thread = utils.FunctionThread(function=omdb.get_movie_info, - param=self.info.get_property("imdb_id")) - self.omdb_thread.start() - sets_thread.start() - self.info.update_properties( - imagetools.blur(self.info.get_art("thumb"))) - if not self.info.get_info("dbid"): - self.info.set_art("poster", utils.get_file( - self.info.get_art("poster"))) - sets_thread.join() - self.info.update_properties( - {"set.%s" % k: v for k, v in list(sets_thread.setinfo.items())}) - set_ids = [item.get_property("id") for item in sets_thread.listitems] - self.lists["similar"] = [i for i in self.lists["similar"] - if i.get_property("id") not in set_ids] - self.lists["sets"] = sets_thread.listitems - - def onInit(self): - super(DialogMovieInfo, self).onInit() - super(DialogMovieInfo, self).update_states() - self.get_youtube_vids("%s %s, movie" % (self.info.label, - self.info.get_info("year"))) - self.set_omdb_infos_async() - - def onClick(self, control_id): - super(DialogMovieInfo, self).onClick(control_id) - ch.serve(control_id, self) - - def set_buttons(self): - super(DialogMovieInfo, self).set_buttons() - condition = self.info.get_info("dbid") and int( - self.info.get_property("percentplayed")) > 0 - self.set_visible(ID_BUTTON_PLAY_RESUME, condition) - self.set_visible(ID_BUTTON_PLAY_NORESUME, self.info.get_info("dbid")) - self.set_visible(ID_BUTTON_TRAILER, self.info.get_info("trailer")) - self.set_visible(ID_BUTTON_SETRATING, True) - self.set_visible(ID_BUTTON_RATED, True) - self.set_visible(ID_BUTTON_ADDTOLIST, True) - self.set_visible(ID_BUTTON_OPENLIST, True) - - @ch.click(ID_BUTTON_TRAILER) - def youtube_button(self, control_id): - wm.play_youtube_video(youtube_id=self.info.get_property("trailer"), - listitem=self.info.get_listitem()) - - @ch.click(ID_LIST_STUDIOS) - def company_list(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_companies", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_REVIEWS) - def reviews_list(self, control_id): - author = self.FocusedItem(control_id).getProperty("author") - text = "[B]%s[/B][CR]%s" % (author, - self.FocusedItem(control_id).getProperty("content")) - xbmcgui.Dialog().textviewer(heading=addon.LANG(207), - text=text) - - @ch.click(ID_LIST_KEYWORDS) - def keyword_list(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_keywords", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_GENRES) - def genre_list(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_genres", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_CERTS) - def cert_list(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("iso_3166_1"), - "type": "certification_country", - "label": self.FocusedItem(control_id).getProperty("iso_3166_1")}, - {"id": self.FocusedItem(control_id).getProperty("certification"), - "type": "certification", - "label": self.FocusedItem(control_id).getProperty("certification")}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_LISTS) - def movielists_list(self, control_id): - wm.open_video_list(mode="list", - list_id=self.FocusedItem( - control_id).getProperty("id"), - filter_label=self.FocusedItem(control_id).getLabel()) - - @ch.click(ID_BUTTON_OPENLIST) - def open_list_button(self, control_id): - busy.show_busy() - movie_lists = tmdb.get_account_lists() - listitems = ["%s (%i)" % (i["name"], i["item_count"]) - for i in movie_lists] - listitems = [addon.LANG(32134), addon.LANG(32135)] + listitems - busy.hide_busy() - index = xbmcgui.Dialog().select(addon.LANG(32136), listitems) - if index == -1: - pass - elif index < 2: - wm.open_video_list(mode="favorites" if index == 0 else "rating") - else: - wm.open_video_list(mode="list", - list_id=movie_lists[index - 2]["id"], - filter_label=movie_lists[index - 2]["name"], - force=True) - - @ch.click(ID_BUTTON_ADDTOLIST) - def add_to_list_button(self, control_id): - busy.show_busy() - account_lists = tmdb.get_account_lists() - listitems = ["%s (%i)" % (i["name"], i["item_count"]) - for i in account_lists] - listitems.insert(0, addon.LANG(32139)) - listitems.append(addon.LANG(32138)) - busy.hide_busy() - index = xbmcgui.Dialog().select(heading=addon.LANG(32136), - list=listitems) - if index == 0: - listname = xbmcgui.Dialog().input(heading=addon.LANG(32137), - type=xbmcgui.INPUT_ALPHANUM) - if not listname: - return None - list_id = tmdb.create_list(listname) - xbmc.sleep(1000) - tmdb.change_list_status(list_id=list_id, - movie_id=self.info.get_property("id"), - status=True) - elif index == len(listitems) - 1: - if tmdb.remove_list_dialog(tmdb.handle_lists(account_lists)): - self.update_states() - elif index > 0: - tmdb.change_list_status( - account_lists[index - 1]["id"], self.info.get_property("id"), True) - self.update_states() - - @ch.click(ID_BUTTON_RATED) - def rating_button(self, control_id): - wm.open_video_list(mode="rating") - - @ch.click(ID_BUTTON_PLAY_RESUME) - def play_noresume_button(self, control_id): - self.exit_script() - xbmc.executebuiltin("Dialog.Close(movieinformation)") - kodijson.play_media("movie", self.info["dbid"], True) - - @ch.click(ID_BUTTON_PLAY_NORESUME) - def play_resume_button(self, control_id): - self.exit_script() - xbmc.executebuiltin("Dialog.Close(movieinformation)") - kodijson.play_media("movie", self.info["dbid"], False) - - def get_manage_options(self): - options = [] - movie_id = self.info.get_info("dbid") - imdb_id = self.info.get_property("imdb_id") - # if movie_id: - # call = "RunScript(script.artwork.downloader,mediatype=movie,dbid={}%s)".format(movie_id) - # options += [(addon.LANG(413), call % ",mode=gui"), - # (addon.LANG(14061), call % ""), - # (addon.LANG(32101), call % ",mode=custom,extrathumbs"), - # (addon.LANG(32100), call % ",mode=custom")] - # else: - options += [(addon.LANG(32165), "RunPlugin(plugin://plugin.video.couchpotato_manager/movies/add?imdb_id=%s)" % imdb_id), - (addon.LANG(32170), "RunPlugin(plugin://plugin.video.trakt_list_manager/watchlist/movies/add?imdb_id=%s)" % imdb_id)] - options.append( - (addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")) - return options - - def update_states(self): - xbmc.sleep(2000) # delay because MovieDB takes some time to update - info = tmdb.get_movie(movie_id=self.info.get_property("id"), - cache_days=0) - self.states = info.get("account_states") - super(DialogMovieInfo, self).update_states() - - @utils.run_async - def set_omdb_infos_async(self) -> None: - """ sets home window properties such as tomato for OMDb response items - """ - self.omdb_thread.join() - utils.dict_to_windowprops(data=self.omdb_thread.listitems, - prefix="omdb.", - window_id=self.window_id) - - -class SetItemsThread(threading.Thread): - - def __init__(self, set_id=""): - threading.Thread.__init__(self) - self.set_id = set_id - - def run(self): - if self.set_id: - self.listitems, self.setinfo = tmdb.get_set_movies(self.set_id) - else: - self.listitems = [] - self.setinfo = {} diff --git a/script.extendedinfo/resources/lib/dialogs/DialogSeasonInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogSeasonInfo.py deleted file mode 100644 index 600f3d8e2..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogSeasonInfo.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -from kutils import ActionHandler, addon, imagetools, utils -from resources.lib import TheMovieDB as tmdb - -from .DialogVideoInfo import DialogVideoInfo - -ch = ActionHandler() - - -class DialogSeasonInfo(DialogVideoInfo): - TYPE = "Season" - LISTS = [(1000, "actors"), - (750, "crew"), - (2000, "episodes"), - (1150, "videos"), - (1250, "images"), - (1350, "backdrops")] - - def __init__(self, *args, **kwargs): - super(DialogSeasonInfo, self).__init__(*args, **kwargs) - self.tvshow_id = kwargs.get('id') - data = tmdb.extended_season_info(tvshow_id=self.tvshow_id, - season_number=kwargs.get('season')) - if not data: - return None - self.info, self.lists = data - if not self.info.get_info("dbid"): # need to add comparing for seasons - poster = utils.get_file(url=self.info.get_art("poster")) - self.info.set_art("poster", poster) - self.info.update_properties( - imagetools.blur(self.info.get_art("poster"))) - - def onInit(self): - self.get_youtube_vids("%s %s tv" % (self.info.get_info( - "tvshowtitle"), self.info.get_info('title'))) - super(DialogSeasonInfo, self).onInit() - - def onClick(self, control_id): - super(DialogSeasonInfo, self).onClick(control_id) - ch.serve(control_id, self) - - def get_manage_options(self): - return [(addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")] diff --git a/script.extendedinfo/resources/lib/dialogs/DialogTVShowInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogTVShowInfo.py deleted file mode 100644 index cd52cde9f..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogTVShowInfo.py +++ /dev/null @@ -1,163 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmc -import xbmcgui - -from kutils import ActionHandler, addon, imagetools, utils -from resources.lib import TheMovieDB as tmdb -from resources.lib.WindowManager import wm - -from .DialogVideoInfo import DialogVideoInfo - -ID_LIST_SIMILAR = 150 -ID_LIST_SEASONS = 250 -ID_LIST_NETWORKS = 1450 -ID_LIST_STUDIOS = 550 -ID_LIST_CERTS = 650 -ID_LIST_CREW = 750 -ID_LIST_GENRES = 850 -ID_LIST_KEYWORDS = 950 -ID_LIST_ACTORS = 1000 -ID_LIST_VIDEOS = 1150 -ID_LIST_IMAGES = 1250 -ID_LIST_BACKDROPS = 1350 - -ID_BUTTON_BROWSE = 120 -ID_BUTTON_OPENLIST = 6002 -ID_BUTTON_RATED = 6006 - -ch = ActionHandler() - - -class DialogTVShowInfo(DialogVideoInfo): - TYPE = "TVShow" - TYPE_ALT = "tv" - LISTS = [(ID_LIST_SIMILAR, "similar"), - (ID_LIST_SEASONS, "seasons"), - (ID_LIST_NETWORKS, "networks"), - (ID_LIST_STUDIOS, "studios"), - (ID_LIST_CERTS, "certifications"), - (ID_LIST_CREW, "crew"), - (ID_LIST_GENRES, "genres"), - (ID_LIST_KEYWORDS, "keywords"), - (ID_LIST_ACTORS, "actors"), - (ID_LIST_VIDEOS, "videos"), - (ID_LIST_IMAGES, "images"), - (ID_LIST_BACKDROPS, "backdrops")] - - def __init__(self, *args, **kwargs): - super(DialogTVShowInfo, self).__init__(*args, **kwargs) - data = tmdb.extended_tvshow_info(tvshow_id=kwargs.get('tmdb_id'), - dbid=kwargs.get('dbid')) - if not data: - return None - self.info, self.lists, self.states = data - if not self.info.get_info("dbid"): - self.info.set_art("poster", utils.get_file( - self.info.get_art("poster"))) - self.info.update_properties( - imagetools.blur(self.info.get_art("poster"))) - - def onInit(self): - self.get_youtube_vids("%s tv" % (self.info.get_info("title"))) - super(DialogTVShowInfo, self).onInit() - super(DialogTVShowInfo, self).update_states() - - def onClick(self, control_id): - super(DialogTVShowInfo, self).onClick(control_id) - ch.serve(control_id, self) - - def set_buttons(self): - self.set_visible(ID_BUTTON_BROWSE, self.get_info("dbid")) - self.set_visible(ID_BUTTON_OPENLIST, self.logged_in) - self.set_visible(ID_BUTTON_RATED, True) - - @ch.click(ID_BUTTON_BROWSE) - def browse_tvshow(self, control_id): - self.exit() - xbmc.executebuiltin("Dialog.Close(all)") - xbmc.executebuiltin( - "ActivateWindow(videos,videodb://tvshows/titles/%s/)" % self.info.get_info("dbid")) - - @ch.click(ID_LIST_SEASONS) - def open_season_dialog(self, control_id): - info = self.FocusedItem(control_id).getVideoInfoTag() - wm.open_season_info(tvshow_id=self.info.get_property("id"), - season=info.getSeason(), - tvshow=self.info.get_info("title")) - - @ch.click(ID_LIST_STUDIOS) - def open_company_info(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_companies", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_KEYWORDS) - def open_keyword_info(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_keywords", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters) - - @ch.click(ID_LIST_GENRES) - def open_genre_info(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_genres", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters, - media_type="tv") - - @ch.click(ID_LIST_NETWORKS) - def open_network_info(self, control_id): - filters = [{"id": self.FocusedItem(control_id).getProperty("id"), - "type": "with_networks", - "label": self.FocusedItem(control_id).getLabel()}] - wm.open_video_list(filters=filters, - media_type="tv") - - def get_manage_options(self): - options = [] - title = self.info.get_info("tvshowtitle") - dbid = self.info.get_info("dbid") - if dbid: - call = "RunScript(script.artwork.downloader,mediatype=tv,dbid={}%s)".format( - dbid) - options += [(addon.LANG(413), call % (",mode=gui")), - (addon.LANG(14061), call % ("")), - (addon.LANG(32101), call % - (",mode=custom,extrathumbs")), - (addon.LANG(32100), call % (",mode=custom"))] - else: - options += [(addon.LANG(32166), - "RunPlugin(plugin://plugin.video.sickrage?action=addshow&show_name=%s)" % title)] - options.append( - (addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")) - return options - - @ch.click(ID_BUTTON_OPENLIST) - def open_list(self, control_id): - index = xbmcgui.Dialog().select(heading=addon.LANG(32136), - list=[addon.LANG(32144), addon.LANG(32145)]) - if index == 0: - wm.open_video_list(media_type="tv", - mode="favorites") - elif index == 1: - wm.open_video_list(mode="rating", - media_type="tv") - - @ch.click(ID_BUTTON_RATED) - def open_rated_items(self, control_id): - wm.open_video_list(mode="rating", - media_type="tv") - - def update_states(self): - xbmc.sleep(2000) # delay because MovieDB takes some time to update - info = tmdb.get_tvshow(tvshow_id=self.info.get_property("id"), - cache_days=0) - self.states = info.get("account_states") - super(DialogTVShowInfo, self).update_states() diff --git a/script.extendedinfo/resources/lib/dialogs/DialogVideoInfo.py b/script.extendedinfo/resources/lib/dialogs/DialogVideoInfo.py deleted file mode 100644 index e9799d886..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogVideoInfo.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmc -import xbmcgui - -from kutils import ActionHandler, addon, utils -from resources.lib import TheMovieDB as tmdb - -from .DialogBaseInfo import DialogBaseInfo - -BUTTONS = {8, 9, 10, 6001, 6002, 6003, 6005, 6006} - -ID_BUTTON_PLOT = 132 -ID_BUTTON_MANAGE = 445 -ID_BUTTON_SETRATING = 6001 -ID_BUTTON_FAV = 6003 - -ch = ActionHandler() - - -class DialogVideoInfo(DialogBaseInfo): - - def __init__(self, *args, **kwargs): - super(DialogVideoInfo, self).__init__(*args, **kwargs) - - def onClick(self, control_id): - super(DialogVideoInfo, self).onClick(control_id) - ch.serve(control_id, self) - - def set_buttons(self): - for button_id in BUTTONS: - self.set_visible(button_id, False) - - @ch.click(ID_BUTTON_PLOT) - def show_plot(self, control_id): - xbmcgui.Dialog().textviewer(heading=addon.LANG(32037), - text=self.info.get_info("plot")) - - def get_manage_options(self): - return [] - - def get_identifier(self): - return self.info.get_property("id") - - @ch.click(ID_BUTTON_MANAGE) - def show_manage_dialog(self, control_id): - options = self.get_manage_options() - index = xbmcgui.Dialog().select(heading=addon.LANG(32133), - list=[i[0] for i in options]) - if index == -1: - return None - for item in options[index][1].split("||"): - xbmc.executebuiltin(item) - - @ch.click(ID_BUTTON_FAV) - def change_list_status(self, control_id): - tmdb.change_fav_status(media_id=self.info.get_property("id"), - media_type=self.TYPE_ALT, - status=str(not bool(self.states["favorite"])).lower()) - self.update_states() - - @ch.click(ID_BUTTON_SETRATING) - def set_rating_dialog(self, control_id): - preselect = int(self.states["rated"]["value"]) if ( - self.states and self.states.get("rated")) else -1 - rating = utils.input_userrating(preselect=preselect) - if rating == -1: - return None - if tmdb.set_rating(media_type=self.TYPE_ALT, - media_id=self.get_identifier(), - rating=rating, - dbid=self.info.get_info("dbid")): - self.setProperty("rated", str(rating) if rating > 0 else "") - self.update_states() diff --git a/script.extendedinfo/resources/lib/dialogs/DialogVideoList.py b/script.extendedinfo/resources/lib/dialogs/DialogVideoList.py deleted file mode 100644 index 29e7a87d2..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogVideoList.py +++ /dev/null @@ -1,475 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import xbmc -import xbmcgui - -from kutils import (ActionHandler, DialogBaseList, addon, busy, confirmdialog, - selectdialog, utils) -from resources.lib import TheMovieDB as tmdb -from resources.lib.WindowManager import wm - -ID_BUTTON_SORT = 5001 -ID_BUTTON_GENREFILTER = 5002 -ID_BUTTON_YEARFILTER = 5003 -ID_BUTTON_ORDER = 5004 -ID_BUTTON_CERTFILTER = 5006 -ID_BUTTON_ACTORFILTER = 5008 -ID_BUTTON_KEYWORDFILTER = 5009 -ID_BUTTON_COMPANYFILTER = 5010 -ID_BUTTON_RUNTIMEFILTER = 5011 -ID_BUTTON_VOTECOUNTFILTER = 5012 -ID_BUTTON_ACCOUNT = 7000 - -ch = ActionHandler() - -include_adult: bool = addon.setting("include_adults").lower() - - -def get_window(window_type): - """[summary] - - Args: - window_type (class instance): xbmc XML dialog window or - xbmc XML window objects - - Returns: - [DialogVideoList]: a new XML dialog or window - """ - - class DialogVideoList(DialogBaseList, window_type): - - TYPES = ["movie", "tv"] - - FILTERS = {"certification_country": addon.LANG(32153), - "certification": addon.LANG(32127), - "year": addon.LANG(562), - "with_genres": addon.LANG(135), - "with_people": addon.LANG(32156), - "with_companies": addon.LANG(20388), - "with_networks": addon.LANG(32152), - "with_keywords": addon.LANG(32114), - "first_air_date": addon.LANG(20416), - "with_runtime": xbmc.getLocalizedString(2050), - "primary_release_date": addon.LANG(345), - "vote_count": addon.LANG(32111)} - - TRANSLATIONS = {"movie": addon.LANG(20338), - "tv": addon.LANG(20364), - "person": addon.LANG(32156)} - - SORTS = {"movie": {"popularity": addon.LANG(32110), - "release_date": addon.LANG(172), - "revenue": addon.LANG(32108), - # "Release Date": "primary_release_date", - "original_title": addon.LANG(20376), - "vote_average": addon.LANG(32112), - "vote_count": addon.LANG(32111)}, - "tv": {"popularity": addon.LANG(32110), - "first_air_date": addon.LANG(20416), - "vote_average": addon.LANG(32112)}, - "favorites": {"created_at": addon.LANG(32157)}, - "list": {"created_at": addon.LANG(32157)}, - "rating": {"created_at": addon.LANG(32157)}} - - LABEL2 = {"popularity": lambda x: x.get_property("popularity"), - "release_date": lambda x: x.get_info("premiered"), - "revenue": lambda x: x.get_info("genre"), - "vote_average": lambda x: x.get_info("rating"), - "vote_count": lambda x: "{} {}".format(x.get_info("votes"), addon.LANG(32082)), - "first_air_date": lambda x: x.get_info("premiered"), - "created_at": lambda x: x.get_property("created_at"), - "original_title": lambda x: x.get_info("originaltitle")} - - @busy.set_busy - def __init__(self, *args, **kwargs): - self.type = kwargs.get('type', "movie") - self.list_id = kwargs.get("list_id", False) - self.logged_in = tmdb.Login.check_login() - super(DialogVideoList, self).__init__(*args, **kwargs) - - def onClick(self, control_id): - super(DialogVideoList, self).onClick(control_id) - ch.serve(control_id, self) - - def onAction(self, action): - super(DialogVideoList, self).onAction(action) - ch.serve_action(action, self.getFocusId(), self) - - def update_ui(self): - super(DialogVideoList, self).update_ui() - self.getControl(ID_BUTTON_CERTFILTER).setVisible(self.type != "tv") - self.getControl(ID_BUTTON_ACTORFILTER).setVisible( - self.type != "tv") - self.getControl(ID_BUTTON_KEYWORDFILTER).setVisible( - self.type != "tv") - self.getControl(ID_BUTTON_COMPANYFILTER).setVisible( - self.type != "tv") - - @ch.context("tvshow") - @ch.context("movie") - def context_menu(self, control_id): - item_id = self.FocusedItem(control_id).getProperty("id") - media_type = self.FocusedItem( - control_id).getVideoInfoTag().getMediaType() - listitems = [addon.LANG(32169)] if media_type == "tvshow" else [ - addon.LANG(32113)] - if self.logged_in: - listitems += [addon.LANG(14076)] - if not self.type == "tv": - listitems += [addon.LANG(32107)] - if self.mode == "list": - listitems += [addon.LANG(32035)] - index = xbmcgui.Dialog().contextmenu(list=listitems) - if index == 0: - # HACK until we can get userrating from listitem via python - rating = utils.get_infolabel("listitem.userrating") - rating = utils.input_userrating( - preselect=int(rating) if rating.isdigit() else -1) - if rating == -1: - return None - if tmdb.set_rating(media_type="tv" if media_type == "tvshow" else "movie", - media_id=item_id, - rating=rating, - dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()): - xbmc.sleep(2000) - self.update(force_update=True) - self.setCurrentListPosition(self.position) - elif index == 1: - tmdb.change_fav_status(media_id=item_id, - media_type=self.type, - status="true") - elif index == 2: - self.list_dialog(item_id) - elif index == 3: - tmdb.change_list_status(list_id=self.list_id, - movie_id=item_id, - status=False) - self.update(force_update=True) - self.setCurrentListPosition(self.position) - - def list_dialog(self, movie_id): - busy.show_busy() - listitems = [addon.LANG(32139)] - account_lists = tmdb.get_account_lists() - listitems += ["%s (%i)" % (i["name"], i["item_count"]) - for i in account_lists] - listitems.append(addon.LANG(32138)) - busy.hide_busy() - index = xbmcgui.Dialog().select(heading=addon.LANG(32136), - list=listitems) - if index == 0: - listname = xbmcgui.Dialog().input(heading=addon.LANG(32137), - type=xbmcgui.INPUT_ALPHANUM) - if listname: - xbmc.sleep(1000) - tmdb.change_list_status(list_id=tmdb.create_list(listname), - movie_id=movie_id, - status=True) - elif index == len(listitems) - 1: - tmdb.remove_list_dialog(tmdb.handle_lists(account_lists)) - elif index > 0: - tmdb.change_list_status(list_id=account_lists[index - 1]["id"], - movie_id=movie_id, - status=True) - - @property - def sort_key(self): - return self.mode if self.mode in ["favorites", "rating", "list"] else self.type - - @property - def default_sort(self): - return "created_at" if self.mode in ["favorites", "rating", "list"] else "popularity" - - @ch.click(ID_BUTTON_SORT) - def get_sort_type(self, control_id): - if not self.choose_sort_method(self.sort_key): - return None - if self.sort == "vote_average": - self.add_filter(key="vote_count.gte", - value="10", - label="10", - reset=False) - self.update() - - def add_filter(self, **kwargs): - key = kwargs["key"].replace(".gte", "").replace(".lte", "") - kwargs["typelabel"] = self.FILTERS[key] - if kwargs["key"].endswith(".lte"): - kwargs["label"] = "< %s" % kwargs["label"] - if kwargs["key"].endswith(".gte"): - kwargs["label"] = "> %s" % kwargs["label"] - super(DialogVideoList, self).add_filter(force_overwrite=kwargs["key"].endswith((".gte", ".lte")), - **kwargs) - - @ch.click(ID_BUTTON_ORDER) - def toggle_order(self, control_id): - self.order = "desc" if self.order == "asc" else "asc" - self.update() - - @ch.click(ID_BUTTON_ACCOUNT) - def open_account_menu(self, control_id): - if self.type == "tv": - listitems = [addon.LANG(32145)] - if self.logged_in: - listitems.append(addon.LANG(32144)) - else: - listitems = [addon.LANG(32135)] - if self.logged_in: - listitems.append(addon.LANG(32134)) - busy.show_busy() - if self.logged_in: - account_lists = tmdb.get_account_lists() - listitems += ["%s (%i)" % (i["name"], i["item_count"]) - for i in account_lists] - busy.hide_busy() - index = xbmcgui.Dialog().select(heading=addon.LANG(32136), - list=listitems) - if index == -1: - pass - elif index == 0: - self.set_sort("created_at") - self.filters = [] - self.reset("rating") - elif index == 1: - self.set_sort("created_at") - self.filters = [] - self.reset("favorites") - else: - self.close() - dialog = wm.open_video_list(filters=[], - mode="list", - list_id=account_lists[index - 2]["id"], - filter_label=account_lists[index - 2]["name"]) - dialog.doModal() - - @ch.click(ID_BUTTON_GENREFILTER) - def set_genre_filter(self, control_id): - params = {"language": addon.setting("LanguageID")} - response = tmdb.get_data(url="genre/%s/list" % (self.type), - params=params, - cache_days=100) - selected = [i["id"] - for i in self.filters if i["type"] == "with_genres"] - ids = [item["id"] for item in response["genres"]] - labels = [item["name"] for item in response["genres"]] - preselect = [ids.index(int(i)) - for i in selected[0].split(",")] if selected else [] - indexes = xbmcgui.Dialog().multiselect(heading=addon.LANG(32151), - options=labels, - preselect=preselect) - if indexes is None: - return None - self.filters = [ - i for i in self.filters if i["type"] != "with_genres"] - for i in indexes: - self.add_filter(key="with_genres", - value=ids[i], - label=labels[i], - reset=False) - self.reset() - - @ch.click(ID_BUTTON_VOTECOUNTFILTER) - def set_vote_count_filter(self, control_id): - ret = True - if not self.type == "tv": - ret = confirmdialog.open(header=addon.LANG(32151), - text=addon.LANG(32106), - nolabel=addon.LANG(32150), - yeslabel=addon.LANG(32149)) - if ret == -1: - return None - result = xbmcgui.Dialog().input(heading=addon.LANG(32111), - type=xbmcgui.INPUT_NUMERIC) - if result: - self.add_filter(key="vote_count.lte" if ret == 1 else "vote_count.gte", - value=result, - label=result) - - @ch.click(ID_BUTTON_YEARFILTER) - def set_year_filter(self, control_id): - ret = confirmdialog.open(header=addon.LANG(32151), - text=addon.LANG(32106), - nolabel=addon.LANG(32150), - yeslabel=addon.LANG(32149)) - if ret == -1: - return None - result = xbmcgui.Dialog().input(heading=addon.LANG(345), - type=xbmcgui.INPUT_NUMERIC) - if not result: - return None - value = "{}-12-31" if ret == 1 else "{}-01-01" - key = "first_air_date" if self.type == "tv" else "primary_release_date" - self.add_filter(key="%s.%s" % (key, "lte" if ret == 1 else "gte"), - value=value.format(result), - label=result) - - @ch.click(ID_BUTTON_RUNTIMEFILTER) - def set_runtime_filter(self, control_id): - ret = confirmdialog.open(header=addon.LANG(32151), - text=addon.LANG(32106), - nolabel=addon.LANG(32150), - yeslabel=addon.LANG(32149)) - if ret == -1: - return None - result = xbmcgui.Dialog().input(heading=xbmc.getLocalizedString(2050), - type=xbmcgui.INPUT_NUMERIC) - if not result: - return None - self.add_filter(key="with_runtime.%s" % ("lte" if ret == 1 else "gte"), - value=result, - label="{} min".format(result)) - - @ch.click(ID_BUTTON_ACTORFILTER) - def set_actor_filter(self, control_id): - result = xbmcgui.Dialog().input(heading=addon.LANG(16017), - type=xbmcgui.INPUT_ALPHANUM) - if not result or result == -1: - return None - response = tmdb.get_person_info(result) - if not response: - return None - self.add_filter(key="with_people", - value=response["id"], - label=response["name"]) - - @ch.info("movie") - @ch.click_by_type("movie") - def open_movie(self, control_id): - wm.open_movie_info(movie_id=self.FocusedItem(control_id).getProperty("id"), - dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) - - @ch.info("tvshow") - @ch.click_by_type("tvshow") - def open_tvshow(self, control_id): - wm.open_tvshow_info(tmdb_id=self.FocusedItem(control_id).getProperty("id"), - dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) - - @ch.info("artist") - @ch.click_by_type("artist") - def open_media(self, control_id): - wm.open_actor_info(actor_id=self.FocusedItem( - control_id).getProperty("id")) - - @ch.click(ID_BUTTON_COMPANYFILTER) - def set_company_filter(self, control_id): - result = xbmcgui.Dialog().input(heading=addon.LANG(16017), - type=xbmcgui.INPUT_ALPHANUM) - if not result or result < 0: - return None - items = tmdb.search_companies(result) - if len(items) > 1: - index = selectdialog.open(header=addon.LANG(32151), - listitems=items) - if index > -1: - item = items[index] - elif items: - item = items[0] - else: - utils.notify("No company found") - self.add_filter(key="with_companies", - value=item.get_property("id"), - label=item.get_label()) - - @ch.click(ID_BUTTON_KEYWORDFILTER) - def set_keyword_filter(self, control_id): - result = xbmcgui.Dialog().input(heading=addon.LANG(16017), - type=xbmcgui.INPUT_ALPHANUM) - if not result or result == -1: - return None - keywords = tmdb.get_keywords(result) - if not keywords: - return None - if len(keywords) > 1: - index = xbmcgui.Dialog().select(heading=addon.LANG(32114), - list=[item["name"] for item in keywords]) - keyword = keywords[index] if index > -1 else None - if not keyword: - return None - else: - keyword = keywords[0] - self.add_filter(key="with_keywords", - value=keyword["id"], - label=keyword["name"]) - - @ch.click(ID_BUTTON_CERTFILTER) - def set_certification_filter(self, control_id): - response = tmdb.get_certification_list(self.type) - countries = [key for key in list(response.keys())] - index = xbmcgui.Dialog().select(heading=addon.LANG(21879), - list=countries) - if index == -1: - return None - country = countries[index] - certs = ["%s - %s" % (i["certification"], i["meaning"]) - for i in response[country]] - index = xbmcgui.Dialog().select(heading=addon.LANG(32151), - list=certs) - if index == -1: - return None - cert = certs[index].split(" - ")[0] - self.add_filter(key="certification_country", - value=country, - label=country, - reset=False) - self.add_filter(key="certification", - value=cert, - label=cert) - - def fetch_data(self, force=False): # TODO: rewrite - sort_by = self.sort + "." + self.order - temp = "tv" if self.type == "tv" else "movies" - #utils.log('DialogVideoList fetch_data args: sort_by {} temp {} mode {}'.format(sort_by, temp, self.mode)) #debug - if self.mode == "search": - self.filter_label = addon.LANG( - 32146) % self.search_str if self.search_str else "" - return tmdb.multi_search(search_str=self.search_str, - page=self.page, - cache_days=0 if force else 2) - elif self.mode == "list": - return tmdb.get_list_movies(list_id=self.list_id, - force=force) - elif self.mode == "favorites": - self.filter_label = addon.LANG( - 32144) if self.type == "tv" else addon.LANG(32134) - return tmdb.get_fav_items(media_type=temp, - sort_by=sort_by, - page=self.page) - elif self.mode == "rating": - self.filter_label = addon.LANG( - 32145) if self.type == "tv" else addon.LANG(32135) - return tmdb.get_rated_media_items(media_type=temp, - sort_by=sort_by, - page=self.page, - cache_days=0) - else: - self.set_filter_label() - params = {"sort_by": sort_by, - "language": addon.setting("LanguageID"), - "page": self.page, - "include_adult": include_adult} - filters = {item["type"]: item["id"] for item in self.filters} - response = tmdb.get_data(url="discover/%s" % (self.type), - params=utils.merge_dicts( - params, filters), - cache_days=0 if force else 2) - - if not response["results"]: - utils.notify(addon.LANG(284)) - return None - if self.type == "movie": - itemlist = tmdb.handle_movies(results=response["results"], - local_first=False, - sortkey=None) - else: - itemlist = tmdb.handle_tvshows(results=response["results"], - local_first=False, - sortkey=None) - itemlist.set_totals(response["total_results"]) - itemlist.set_total_pages(response["total_pages"]) - return itemlist - - return DialogVideoList diff --git a/script.extendedinfo/resources/lib/dialogs/DialogYoutubeList.py b/script.extendedinfo/resources/lib/dialogs/DialogYoutubeList.py deleted file mode 100644 index 2044d9bc6..000000000 --- a/script.extendedinfo/resources/lib/dialogs/DialogYoutubeList.py +++ /dev/null @@ -1,238 +0,0 @@ -# -*- coding: utf8 -*- - -# Copyright (C) 2015 - Philipp Temminghoff -# Modifications copyright (C) 2022 - Scott Smart -# This program is Free Software see LICENSE file for details - -import datetime - -import xbmcgui - -from kutils import (ActionHandler, DialogBaseList, addon, busy, utils, windows, - youtube) -from resources.lib.WindowManager import wm - -ch = ActionHandler() - -ID_BUTTON_SORTTYPE = 5001 -ID_BUTTON_PUBLISHEDFILTER = 5002 -ID_BUTTON_LANGUAGEFILTER = 5003 -ID_BUTTON_DIMENSIONFILTER = 5006 -ID_BUTTON_DURATIONFILTER = 5008 -ID_BUTTON_CAPTIONFILTER = 5009 -ID_BUTTON_DEFINITIONFILTER = 5012 -ID_BUTTON_TYPEFILTER = 5013 - - -def get_window(window_type): - - class DialogYoutubeList(DialogBaseList, window_type): - - TYPES = ["video", "playlist", "channel"] - - FILTERS = {"channelId": addon.LANG(19029), - "publishedAfter": addon.LANG(172), - "regionCode": addon.LANG(248), - "videoDimension": addon.LANG(32057), - "videoDuration": addon.LANG(180), - "videoCaption": addon.LANG(287), - "videoDefinition": addon.LANG(32058), - "videoType": "Type", - "relatedToVideoId": addon.LANG(32058)} - - TRANSLATIONS = {"video": addon.LANG(157), - "playlist": addon.LANG(559), - "channel": addon.LANG(19029)} - - SORTS = {"video": {"date": addon.LANG(552), - "rating": addon.LANG(563), - "relevance": addon.LANG(32060), - "title": addon.LANG(369), - "viewCount": addon.LANG(567)}, - "playlist": {"date": addon.LANG(552), - "rating": addon.LANG(563), - "relevance": addon.LANG(32060), - "title": addon.LANG(369), - "videoCount": addon.LANG(32068), - "viewCount": addon.LANG(567)}, - "channel": {"date": addon.LANG(552), - "rating": addon.LANG(563), - "relevance": addon.LANG(32060), - "title": addon.LANG(369), - "videoCount": addon.LANG(32068), - "viewCount": addon.LANG(567)}} - - LABEL2 = {"date": lambda x: x.get_info("date"), - "relevance": lambda x: x.get_property("relevance"), - "title": lambda x: x.get_info("title"), - "viewCount": lambda x: x.get_property("viewCount"), - "videoCount": lambda x: x.get_property("videoCount"), - "rating": lambda x: x.get_info("rating")} - - @busy.set_busy - def __init__(self, *args, **kwargs): - self.type = kwargs.get('type', "video") - super(DialogYoutubeList, self).__init__(*args, **kwargs) - - def onClick(self, control_id): - super(DialogYoutubeList, self).onClick(control_id) - ch.serve(control_id, self) - - def onAction(self, action): - super(DialogYoutubeList, self).onAction(action) - ch.serve_action(action, self.getFocusId(), self) - - @ch.click_by_type("video") - def main_list_click(self, control_id): - listitem = self.FocusedItem(control_id) - youtube_id = listitem.getProperty("youtube_id") - media_type = listitem.getProperty("type") - if media_type == "channel": - filter_ = [{"id": youtube_id, - "type": "channelId", - "label": listitem.getLabel()}] - wm.open_youtube_list(filters=filter_) - else: - wm.play_youtube_video(youtube_id=youtube_id, - listitem=listitem) - - @ch.click(ID_BUTTON_PUBLISHEDFILTER) - def set_published_filter(self, control_id): - options = [(1, addon.LANG(32062)), - (7, addon.LANG(32063)), - (31, addon.LANG(32064)), - (365, addon.LANG(32065)), - ("custom", addon.LANG(636))] - deltas = [i[0] for i in options] - labels = [i[1] for i in options] - index = xbmcgui.Dialog().select(heading=addon.LANG(32151), - list=labels) - if index == -1: - return None - delta = deltas[index] - if delta == "custom": - delta = xbmcgui.Dialog().input(heading=addon.LANG(32067), - type=xbmcgui.INPUT_NUMERIC) - if not delta: - return None - d = datetime.datetime.now() - datetime.timedelta(int(delta)) - self.add_filter(key="publishedAfter", - value=d.isoformat('T')[:-7] + "Z", - label=labels[index]) - - @ch.click(ID_BUTTON_LANGUAGEFILTER) - def set_language_filter(self, control_id): - options = [("en", "en"), - ("de", "de"), - ("fr", "fr")] - self.choose_filter("regionCode", 32151, options) - - @ch.click(ID_BUTTON_DIMENSIONFILTER) - def set_dimension_filter(self, control_id): - options = [("2d", "2D"), - ("3d", "3D"), - ("any", addon.LANG(593))] - self.choose_filter("videoDimension", 32151, options) - - @ch.click(ID_BUTTON_DURATIONFILTER) - def set_duration_filter(self, control_id): - options = [("long", addon.LANG(33013)), - ("medium", addon.LANG(601)), - ("short", addon.LANG(33012)), - ("any", addon.LANG(593))] - self.choose_filter("videoDuration", 32151, options) - - @ch.click(ID_BUTTON_CAPTIONFILTER) - def set_caption_filter(self, control_id): - options = [("closedCaption", addon.LANG(107)), - ("none", addon.LANG(106)), - ("any", addon.LANG(593))] - self.choose_filter("videoCaption", 287, options) - - @ch.click(ID_BUTTON_DEFINITIONFILTER) - def set_definition_filter(self, control_id): - options = [("high", addon.LANG(419)), - ("standard", addon.LANG(602)), - ("any", addon.LANG(593))] - self.choose_filter("videoDefinition", 169, options) - - @ch.click(ID_BUTTON_TYPEFILTER) - def set_type_filter(self, control_id): - options = [("movie", addon.LANG(20338)), - ("episode", addon.LANG(20359)), - ("any", addon.LANG(593))] - self.choose_filter("videoType", 32151, options) - - @ch.click(ID_BUTTON_SORTTYPE) - def get_sort_type(self, control_id): - if not self.choose_sort_method(self.type): - return None - self.update() - - @ch.context("video") - def context_menu(self, control_id): - listitem = self.FocusedItem(control_id) - if self.type == "video": - more_vids = "{} [B]{}[/B]".format(addon.LANG(32081), - listitem.getProperty("channel_title")) - index = xbmcgui.Dialog().contextmenu( - list=[addon.LANG(32069), more_vids]) - if index < 0: - return None - elif index == 0: - filter_ = [{"id": listitem.getProperty("youtube_id"), - "type": "relatedToVideoId", - "label": listitem.getLabel()}] - wm.open_youtube_list(filters=filter_) - elif index == 1: - filter_ = [{"id": listitem.getProperty("channel_id"), - "type": "channelId", - "label": listitem.getProperty("channel_title")}] - wm.open_youtube_list(filters=filter_) - - def update_ui(self): - is_video = self.type == "video" - self.getControl(ID_BUTTON_DIMENSIONFILTER).setVisible(is_video) - self.getControl(ID_BUTTON_DURATIONFILTER).setVisible(is_video) - self.getControl(ID_BUTTON_CAPTIONFILTER).setVisible(is_video) - self.getControl(ID_BUTTON_DEFINITIONFILTER).setVisible(is_video) - super(DialogYoutubeList, self).update_ui() - - @property - def default_sort(self): - return "relevance" - - def add_filter(self, **kwargs): - kwargs["typelabel"] = self.FILTERS[kwargs["key"]] - super(DialogYoutubeList, self).add_filter(force_overwrite=True, - **kwargs) - - def fetch_data(self, force=False): - self.set_filter_label() - if self.search_str: - self.filter_label = addon.LANG(32146) % ( - self.search_str) + " " + self.filter_label - user_key = addon.setting("Youtube API Key") - return youtube.search(search_str=self.search_str, - orderby=self.sort, - extended=True, - filters={item["type"]: item["id"] - for item in self.filters}, - media_type=self.type, - page=self.page_token, - api_key=user_key) - - return DialogYoutubeList - - -def open(self, search_str="", filters=None, sort="relevance", filter_label="", media_type="video"): - """ - open video list, deal with window stack - """ - YouTube = get_window(windows.DialogXML) - dialog = YouTube('script-%s-YoutubeList.xml' % addon.NAME, addon.PATH, - search_str=search_str, - filters=[] if not filters else filters, - filter_label=filter_label, - type=media_type) - return dialog diff --git a/script.extendedinfo/resources/lib/dialogs/dialogactorinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogactorinfo.py new file mode 100644 index 000000000..bd4e6be9e --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogactorinfo.py @@ -0,0 +1,100 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +"""Provides the DialogActorInfo class that implements a dialog +XML window. The Actor Info is added to the window properties +from a kutils VideoItem. Panels of VideoItems are added from +kutils ItemLists and a Youtube list. + +The class hierarchy is: + xbmcgui.Window + -------------- + xbmcgui.WindowXML / xbmcgui.WindowDialogMixin + --------------- + xbmc.WindowDialogXML / kutils.windows.WindowMixin + --------------- + kutils.windows.DialogXML + --------------- + DialogBaseInfo + --------------- + DialogActorInfo + +""" + +import xbmcgui +from resources.kutil131 import ActionHandler, ItemList, addon + +from resources.kutil131 import VideoItem, imagetools, utils +from resources.lib import themoviedb as tmdb + +from .dialogbaseinfo import DialogBaseInfo + +ID_CONTROL_PLOT = 132 + +ch = ActionHandler() + + +class DialogActorInfo(DialogBaseInfo): + """creates a dialog XML window for actor info + + Args: + DialogBaseInfo: handles common window details + """ + TYPE = "Actor" + LISTS = [(150, "movie_roles"), + (250, "tvshow_roles"), + (450, "images"), + (550, "movie_crew_roles"), + (650, "tvshow_crew_roles"), + (750, "tagged_images")] + + def __init__(self, *args, **kwargs): + """Constructs the dialog window with actor info from tmdb + self.info is a ListItem for the actor + self.lists is a list of ListItems for the actor's movies and tv shows + if TMDB actor id is provided, gets extended info from TMDB + + Arguments: + *args: dialog xml filename + this addon path (for cache) + **kwargs: id(int): the tmdb actor id + Returns: + None: if no tmdb extended actor info available + self.info and self.lists are set from extended actor info + """ + super().__init__(*args, **kwargs) + data: tuple = tmdb.extended_actor_info(actor_id=kwargs.get('id')) + if not data: + return None + self.info, self.lists = data + self.info.update_properties( + imagetools.blur(self.info.get_art("thumb"))) + + def onInit(self, *args, **kwargs): + """callback function from Kodi when window is opened + Also calls onInit in parent classes to set all info in the + dialog window + """ + self.get_youtube_vids(self.info.label) + super().onInit() + + def onClick(self, control_id): + """callback function from Kodi when control in window is + clicked + + Args: + control_id (int): id of window control that was clicked + """ + super().onClick(control_id) + ch.serve(control_id, self) + + @ch.click(ID_CONTROL_PLOT) + def show_plot(self, control_id): + """Opens info dialog textbox in a textviewer + + Args: + control_id (int): window control id that was clicked + """ + xbmcgui.Dialog().textviewer(heading=addon.LANG(32037), + text=self.info.get_property("biography")) diff --git a/script.extendedinfo/resources/lib/dialogs/dialogbaseinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogbaseinfo.py new file mode 100644 index 000000000..3ed1b5789 --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogbaseinfo.py @@ -0,0 +1,313 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +import traceback + +import xbmc +import xbmcgui +from resources.kutil131 import (ActionHandler, addon, kodijson, selectdialog, slideshow, + windows) + +from resources.kutil131 import VideoItem, utils, youtube +from resources.lib import themoviedb as tmdb +from resources.lib.windowmanager import wm + +ch = ActionHandler() + +ID_LIST_YOUTUBE = 350 +ID_LIST_IMAGES = 1250 +ID_BUTTON_BOUNCEUP = 20000 +ID_BUTTON_BOUNCEDOWN = 20001 + + +class DialogBaseInfo(windows.DialogXML): + """Class constructs a basic dialog xml window. Subclasses augment for different + dialog types (eg actor info or movie info) + + Args: + windows.DialogXML (DialogXML): a kutils class derived from xbmcgui.WindowXMLDialog + and kutils WindowMixin classes + + Returns: + _type_: _description_ + """ + ACTION_PREVIOUS_MENU = [92, 9] + ACTION_EXIT_SCRIPT = [13, 10] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.logged_in: bool = tmdb.Login.check_login() + self.bouncing = False + self.last_focus = None + self.lists = None + self.states = False + self.yt_listitems = [] + self.info = VideoItem() # kutils listitem + self.last_control = None + self.last_position = None + + def onInit(self, *args, **kwargs): + super().onInit() + # self.set_buttons() + self.info.to_windowprops(window_id=self.window_id) #kutils sets dialog window + #properties from the info VideoItem(listitem) + for container_id, key in self.LISTS: + try: + self.getControl(container_id).reset() + items = [i.get_listitem() for i in self.lists[key]] # lists is a dict of ItemList get_listitem gets xbmc listitem from VideoItem + self.getControl(container_id).addItems(items) + except (IndexError, KeyError) as err: + utils.log(f'Notice: No container with id {container_id} key {key} available due to {err}') + except Exception as err: + utils.log(f'Notice: No container with id {container_id} key {key} available due to {err}') + utils.log(f'traceback for this exception\n{traceback.format_exc()}') + if self.last_control: + self.setFocusId(self.last_control) + if self.last_control and self.last_position: + try: + self.getControl(self.last_control).selectItem( + self.last_position) + except Exception: + pass + addon.set_global("ImageColor", self.info.get_property('ImageColor')) + addon.set_global("ImageFilter", self.info.get_property('ImageFilter')) + addon.set_global("infobackground", self.info.get_art('fanart_small')) + self.setProperty("type", self.TYPE) + self.setProperty("tmdb_logged_in", "true" if self.logged_in else "") + + def onAction(self, action): + ch.serve_action(action, self.getFocusId(), self) + + def onClick(self, control_id: int): + super().onClick(control_id) + ch.serve(control_id, self) + + def onFocus(self, control_id): + if control_id == ID_BUTTON_BOUNCEUP: + if not self.bouncing: + self.bounce("up") + self.setFocusId(self.last_focus) + elif control_id == ID_BUTTON_BOUNCEDOWN: + if not self.bouncing: + self.bounce("down") + self.setFocusId(self.last_focus) + self.last_focus = control_id + + def close(self): + try: + self.last_position = self.getFocus().getSelectedPosition() + except Exception: + self.last_position = None + addon.set_global("infobackground", "") + self.last_control = self.getFocusId() + super().close() + + @utils.run_async + def bounce(self, identifier): + self.bouncing = True + self.setProperty("Bounce.%s" % identifier, "true") + xbmc.sleep(200) + self.clearProperty("Bounce.%s" % identifier) + self.bouncing = False + + @ch.click_by_type("music") + # hack: use "music" until "pictures" got added to core + def open_image(self, control_id): + key = [key for container_id, + key in self.LISTS if container_id == control_id][0] + pos = slideshow.open(listitems=self.lists[key], + index=self.getControl(control_id).getSelectedPosition()) + self.getControl(control_id).selectItem(pos) + + @ch.click_by_type("video") + def play_youtube_video(self, control_id): + wm.play_youtube_video(youtube_id=self.FocusedItem(control_id).getProperty("youtube_id"), + listitem=self.FocusedItem(control_id)) + + @ch.click_by_type("artist") + def open_actor_info(self, control_id): + wm.open_actor_info(actor_id=self.FocusedItem(control_id).getProperty("id"), + name=self.FocusedItem(control_id).getLabel()) + + @ch.click_by_type("movie") + def open_movie_info(self, control_id): + wm.open_movie_info(movie_id=self.FocusedItem(control_id).getProperty("id"), + dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) + + @ch.click_by_type("tvshow") + def open_tvshow_info(self, control_id): + wm.open_tvshow_info(tmdb_id=self.FocusedItem(control_id).getProperty("id"), + dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) + + @ch.click_by_type("episode") + def open_episode_info(self, control_id): + info = self.FocusedItem(control_id).getVideoInfoTag() + wm.open_episode_info(tvshow=self.info.get_info("tvshowtitle"), + tvshow_id=self.tvshow_id, + season=info.getSeason(), + episode=info.getEpisode()) + + @ch.context("music") + def thumbnail_options(self, control_id): + listitem = self.FocusedItem(control_id) + art_type = listitem.getProperty("type") + options = [] + if self.info.get_info("dbid") and art_type == "poster": + options.append(("db_art", addon.LANG(32006))) + if self.info.get_info("dbid") and art_type == "fanart": + options.append(("db_art", addon.LANG(32007))) + movie_id = listitem.getProperty("movie_id") + if movie_id: + options.append(("movie_info", addon.LANG(10524))) + if not options: + return None + action = utils.contextmenu(options=options) + if action == "db_art": + kodijson.set_art(media_type=self.getProperty("type"), + art={art_type: listitem.get_art("original")}, + dbid=self.info.get_info("dbid")) + elif action == "movie_info": + wm.open_movie_info(movie_id=listitem.getProperty("movie_id"), + dbid=listitem.getVideoInfoTag().getDbId()) + + @ch.context("video") + def video_context_menu(self, control_id): + index = xbmcgui.Dialog().contextmenu(list=[addon.LANG(33003)]) + if index == 0: + #utils.download_video(self.FocusedItem( + # control_id).getProperty("youtube_id")) + pass + + @ch.context("movie") + def movie_context_menu(self, control_id): + movie_id = self.FocusedItem(control_id).getProperty("id") + dbid = self.FocusedItem(control_id).getVideoInfoTag().getDbId() + options = [addon.LANG(32113)] + if self.logged_in: + options.append(addon.LANG(32083)) + index = xbmcgui.Dialog().contextmenu(list=options) + if index == 0: + rating = utils.input_userrating() + if rating == -1: + return None + tmdb.set_rating(media_type="movie", + media_id=movie_id, + rating=rating, + dbid=dbid) + xbmc.sleep(2000) + tmdb.get_movie(movie_id=movie_id, + cache_days=0) + elif index == 1: + account_lists = tmdb.get_account_lists() + if not account_lists: + return False + listitems = ["%s (%i)" % (i["name"], i["item_count"]) + for i in account_lists] + i = xbmcgui.Dialog().select(addon.LANG(32136), listitems) + if i > -1: + tmdb.change_list_status(list_id=account_lists[i]["id"], + movie_id=movie_id, + status=True) + + @ch.context("artist") + def person_context_menu(self, control_id): + listitem = self.FocusedItem(control_id) + options = [addon.LANG(32009), addon.LANG(32070)] + credit_id = listitem.getProperty("credit_id") + if credit_id and self.TYPE == "TVShow": + options.append(addon.LANG(32147)) + index = xbmcgui.Dialog().contextmenu(list=options) + if index == 0: + wm.open_actor_info(actor_id=listitem.getProperty("id")) + if index == 1: + filters = [{"id": listitem.getProperty("id"), + "type": "with_people", + "label": listitem.getLabel()}] + wm.open_video_list(filters=filters) + if index == 2: + self.open_credit_dialog(credit_id) + + @ch.context("tvshow") + def tvshow_context_menu(self, control_id): + tvshow_id = self.FocusedItem(control_id).getProperty("id") + dbid = self.FocusedItem(control_id).getVideoInfoTag().getDbId() + credit_id = self.FocusedItem(control_id).getProperty("credit_id") + options = [addon.LANG(32169)] + if credit_id: + options.append(addon.LANG(32147)) + index = xbmcgui.Dialog().contextmenu(list=options) + if index == 0: + rating = utils.input_userrating() + if rating == -1: + return None + tmdb.set_rating(media_type="tvshow", + media_id=tvshow_id, + rating=rating, + dbid=dbid) + xbmc.sleep(2000) + tmdb.get_tvshow(tvshow_id=tvshow_id, + cache_days=0) + if index == 1: + self.open_credit_dialog(credit_id=credit_id) + + @ch.action("parentdir", "*") + @ch.action("parentfolder", "*") + def previous_menu(self, control_id): + onback = self.getProperty("%i_onback" % control_id) + if onback: + xbmc.executebuiltin(onback) + else: + self.close() + + @ch.action("previousmenu", "*") + def exit_script(self, *args): + self.exit() + + # @utils.run_async + def get_youtube_vids(self, search_str): + try: + youtube_list = self.getControl(ID_LIST_YOUTUBE) + except Exception as err: + utils.log(f'DialogBaseInfo.get_youtube_vids threw exception {err}') + return None + if not self.yt_listitems: + user_key = addon.setting("Youtube API Key") + search_str = search_str.replace('-', '') + self.yt_listitems = youtube.search( + search_str, limit=15, api_key=user_key) + if not self.yt_listitems: + return None + vid_ids = [item.get_property( + "key") for item in self.lists["videos"]] if "videos" in self.lists else [] + youtube_list.reset() + youtube_list.addItems( + [i.get_listitem() for i in self.yt_listitems if i.get_property("youtube_id") not in vid_ids]) + + def open_credit_dialog(self, credit_id): + info = tmdb.get_credit_info(credit_id) + listitems = [] + if "seasons" in info["media"]: + listitems += tmdb.handle_seasons(info["media"]["seasons"]) + if "episodes" in info["media"]: + listitems += tmdb.handle_episodes(info["media"]["episodes"]) + if not listitems: + listitems += [{"label": addon.LANG(19055)}] + index = selectdialog.open(header=addon.LANG(32151), + listitems=listitems) + if index == -1: + return None + listitem = listitems[index] + if listitem["mediatype"] == "episode": + wm.open_episode_info(season=listitem["season"], + episode=listitem["episode"], + tvshow_id=info["media"]["id"]) + elif listitem["mediatype"] == "season": + wm.open_season_info(season=listitem["season"], + tvshow_id=info["media"]["id"]) + + def update_states(self): + if not self.states: + return None + utils.dict_to_windowprops(data=tmdb.get_account_props(self.states), + window_id=self.window_id) diff --git a/script.extendedinfo/resources/lib/dialogs/dialogepisodeinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogepisodeinfo.py new file mode 100644 index 000000000..bcc65a90d --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogepisodeinfo.py @@ -0,0 +1,77 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +import xbmc +from resources.kutil131 import ActionHandler, addon, busy + +from resources.kutil131 import imagetools +from resources.lib import themoviedb as tmdb +from resources.lib.windowmanager import wm + +from .dialogvideoinfo import DialogVideoInfo + +ID_BUTTON_RATED = 6006 + +ch = ActionHandler() + + +class DialogEpisodeInfo(DialogVideoInfo): + TYPE = "Episode" + TYPE_ALT = "episode" + LISTS = [(1000, "actors"), + (750, "crew"), + (1150, "videos"), + (1350, "images")] + + @busy.set_busy + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.tvshow_id = kwargs.get('tvshow_id') + tv_info = tmdb.get_tvshow(self.tvshow_id, light=True) + data = tmdb.extended_episode_info(tvshow_id=self.tvshow_id, + season=kwargs.get('season'), + episode=kwargs.get('episode')) + if not data: + return None + self.info, self.lists, self.states = data + self.info.set_info("tvshowtitle", tv_info["name"]) + image_info = imagetools.blur(self.info.get_art("thumb")) + self.info.update_properties(image_info) + + def onInit(self): + super().onInit() + search_str = '{} "Season {}" "Episode {}"'.format(self.info.get_info("tvshowtitle"), + self.info.get_info( + 'season'), + self.info.get_info('episode')) + self.get_youtube_vids(search_str) + super().update_states() + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + @ch.click(ID_BUTTON_RATED) + def open_rating_list(self, control_id): + busy.show_busy() + listitems = tmdb.get_rated_media_items("tv/episodes") + busy.hide_busy() + wm.open_video_list(listitems=listitems) + + def get_identifier(self): + return [self.tvshow_id, + self.info.get_info("season"), + self.info.get_info("episode")] + + def update_states(self): + xbmc.sleep(2000) # delay because MovieDB takes some time to update + info = tmdb.get_episode(tvshow_id=self.tvshow_id, + season=self.info.get_info("season"), + episode=self.info.get_info("episode"), + cache_days=0) + self.states = info.get("account_states") + super().update_states() + + def get_manage_options(self): + return [(addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")] diff --git a/script.extendedinfo/resources/lib/dialogs/dialogmovieinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogmovieinfo.py new file mode 100644 index 000000000..792f7cf0a --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogmovieinfo.py @@ -0,0 +1,291 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +from __future__ import annotations + +import threading + +import xbmc +import xbmcgui +from resources.kutil131 import ActionHandler, addon, busy, kodijson + +from resources.kutil131 import imagetools, utils +from resources.lib import themoviedb as tmdb +from resources.lib import omdb +from resources.lib.windowmanager import wm + +from .dialogvideoinfo import DialogVideoInfo + +ID_LIST_SIMILAR = 150 +ID_LIST_SETS = 250 +ID_LIST_YOUTUBE = 350 +ID_LIST_LISTS = 450 +ID_LIST_STUDIOS = 550 +ID_LIST_CERTS = 650 +ID_LIST_CREW = 750 +ID_LIST_GENRES = 850 +ID_LIST_KEYWORDS = 950 +ID_LIST_ACTORS = 1000 +ID_LIST_REVIEWS = 1050 +ID_LIST_VIDEOS = 1150 +ID_LIST_IMAGES = 1250 +ID_LIST_BACKDROPS = 1350 + +ID_BUTTON_PLAY_NORESUME = 8 +ID_BUTTON_PLAY_RESUME = 9 +ID_BUTTON_TRAILER = 10 +ID_BUTTON_SETRATING = 6001 +ID_BUTTON_OPENLIST = 6002 +ID_BUTTON_ADDTOLIST = 6005 +ID_BUTTON_RATED = 6006 + +ch = ActionHandler() + + +class DialogMovieInfo(DialogVideoInfo): + """ Class that creates a new movie info dialog + + Args: + DialogVideoInfo: The super class + + Returns: + DialogMmovieInfo: an instance + """ + TYPE = "Movie" + TYPE_ALT = "movie" + LISTS = [(ID_LIST_ACTORS, "actors"), + (ID_LIST_SIMILAR, "similar"), + (ID_LIST_SETS, "sets"), + (ID_LIST_LISTS, "lists"), + (ID_LIST_STUDIOS, "studios"), + (ID_LIST_CERTS, "releases"), + (ID_LIST_CREW, "crew"), + (ID_LIST_GENRES, "genres"), + (ID_LIST_KEYWORDS, "keywords"), + (ID_LIST_REVIEWS, "reviews"), + (ID_LIST_VIDEOS, "videos"), + (ID_LIST_IMAGES, "images"), + (ID_LIST_BACKDROPS, "backdrops")] + + # BUTTONS = [ID_BUTTON_OPENLIST, + # ID_BUTTON_ADDTOLIST] + + def __init__(self, *args, **kwargs) -> DialogMovieInfo: + """Creates a new instance of DialogMovieInfo + + kwargs: + id(int): the tmdb movie id + dbid(int): the local Kodi dbid + + Returns: dialogmovieinfo + """ + super().__init__(*args, **kwargs) + data: tuple | None = tmdb.extended_movie_info(movie_id=kwargs.get('id'), + dbid=kwargs.get('dbid')) + if not data: + return None + self.info, self.lists, self.states = data + sets_thread = SetItemsThread(self.info.get_property("set_id")) + self.omdb_thread = utils.FunctionThread(function=omdb.get_movie_info, + param=self.info.get_property("imdb_id")) + self.omdb_thread.start() + sets_thread.start() + self.info.update_properties( + imagetools.blur(self.info.get_art("thumb"))) + if not self.info.get_info("dbid"): + self.info.set_art("poster", utils.get_file( + self.info.get_art("poster"))) + sets_thread.join() + self.info.update_properties( + {f"set.{k}": v for k, v in list(sets_thread.setinfo.items())}) + set_ids = [item.get_property("id") for item in sets_thread.listitems] + if self.lists and self.lists.get('similar'): + self.lists["similar"] = [i for i in self.lists["similar"] + if i.get_property("id") not in set_ids] + self.lists["sets"] = sets_thread.listitems + + def onInit(self): + super().onInit() + super().update_states() + self.get_youtube_vids("%s %s, movie" % (self.info.label, + self.info.get_info("year"))) + self.set_omdb_infos_async() + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def set_buttons(self): + super().set_buttons() + condition = self.info.get_info("dbid") and int( + self.info.get_property("percentplayed")) > 0 + self.set_visible(ID_BUTTON_PLAY_RESUME, condition) + self.set_visible(ID_BUTTON_PLAY_NORESUME, self.info.get_info("dbid")) + self.set_visible(ID_BUTTON_TRAILER, self.info.get_info("trailer")) + self.set_visible(ID_BUTTON_SETRATING, True) + self.set_visible(ID_BUTTON_RATED, True) + self.set_visible(ID_BUTTON_ADDTOLIST, True) + self.set_visible(ID_BUTTON_OPENLIST, True) + + @ch.click(ID_BUTTON_TRAILER) + def youtube_button(self, control_id): + wm.play_youtube_video(youtube_id=self.info.get_property("trailer"), + listitem=self.info.get_listitem()) + + @ch.click(ID_LIST_STUDIOS) + def company_list(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_companies", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_REVIEWS) + def reviews_list(self, control_id): + author = self.FocusedItem(control_id).getProperty("author") + text = "[B]%s[/B][CR]%s" % (author, + self.FocusedItem(control_id).getProperty("content")) + xbmcgui.Dialog().textviewer(heading=addon.LANG(183), + text=text) + + @ch.click(ID_LIST_KEYWORDS) + def keyword_list(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_keywords", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_GENRES) + def genre_list(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_genres", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_CERTS) + def cert_list(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("iso_3166_1"), + "type": "certification_country", + "label": self.FocusedItem(control_id).getProperty("iso_3166_1")}, + {"id": self.FocusedItem(control_id).getProperty("certification"), + "type": "certification", + "label": self.FocusedItem(control_id).getProperty("certification")}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_LISTS) + def movielists_list(self, control_id): + wm.open_video_list(mode="list", + list_id=self.FocusedItem( + control_id).getProperty("id"), + filter_label=self.FocusedItem(control_id).getLabel()) + + @ch.click(ID_BUTTON_OPENLIST) + def open_list_button(self, control_id): + busy.show_busy() + movie_lists = tmdb.get_account_lists() + listitems = ["%s (%i)" % (i["name"], i["item_count"]) + for i in movie_lists] + listitems = [addon.LANG(32134), addon.LANG(32135)] + listitems + busy.hide_busy() + index = xbmcgui.Dialog().select(addon.LANG(32136), listitems) + if index == -1: + pass + elif index < 2: + wm.open_video_list(mode="favorites" if index == 0 else "rating") + else: + wm.open_video_list(mode="list", + list_id=movie_lists[index - 2]["id"], + filter_label=movie_lists[index - 2]["name"], + force=True) + + @ch.click(ID_BUTTON_ADDTOLIST) + def add_to_list_button(self, control_id): + busy.show_busy() + account_lists = tmdb.get_account_lists() + listitems = ["%s (%i)" % (i["name"], i["item_count"]) + for i in account_lists] + listitems.insert(0, addon.LANG(32139)) + listitems.append(addon.LANG(32138)) + busy.hide_busy() + index = xbmcgui.Dialog().select(heading=addon.LANG(32136), + list=listitems) + if index == 0: + listname = xbmcgui.Dialog().input(heading=addon.LANG(32137), + type=xbmcgui.INPUT_ALPHANUM) + if not listname: + return None + list_id = tmdb.create_list(listname) + xbmc.sleep(1000) + tmdb.change_list_status(list_id=list_id, + movie_id=self.info.get_property("id"), + status=True) + elif index == len(listitems) - 1: + if tmdb.remove_list_dialog(tmdb.handle_lists(account_lists)): + self.update_states() + elif index > 0: + tmdb.change_list_status( + account_lists[index - 1]["id"], self.info.get_property("id"), True) + self.update_states() + + @ch.click(ID_BUTTON_RATED) + def rating_button(self, control_id): + wm.open_video_list(mode="rating") + + @ch.click(ID_BUTTON_PLAY_RESUME) + def play_noresume_button(self, control_id): + self.exit_script() + xbmc.executebuiltin("Dialog.Close(movieinformation)") + kodijson.play_media("movie", self.info["dbid"], True) + + @ch.click(ID_BUTTON_PLAY_NORESUME) + def play_resume_button(self, control_id): + self.exit_script() + xbmc.executebuiltin("Dialog.Close(movieinformation)") + kodijson.play_media("movie", self.info["dbid"], False) + + def get_manage_options(self): + options = [] + movie_id = self.info.get_info("dbid") + imdb_id = self.info.get_property("imdb_id") + #options += [(addon.LANG(32165), "RunPlugin(plugin://plugin.video.couchpotato_manager/movies/add?imdb_id=%s)" % imdb_id), + # (addon.LANG(32170), "RunPlugin(plugin://plugin.video.trakt_list_manager/watchlist/movies/add?imdb_id=%s)" % imdb_id)] + options.append( + (addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")) + return options + + def update_states(self): + xbmc.sleep(2000) # delay because MovieDB takes some time to update + info = tmdb.get_movie(movie_id=self.info.get_property("id"), + cache_days=0) + self.states = info.get("account_states") + super().update_states() + + @utils.run_async + def set_omdb_infos_async(self) -> None: + """ sets home window properties such as tomato for OMDb response items + """ + self.omdb_thread.join() + utils.dict_to_windowprops(data=self.omdb_thread.listitems, + prefix="omdb.", + window_id=self.window_id) + + +class SetItemsThread(threading.Thread): + """Thread fetches movies in set from tmdb + + Args: + threading.Thead (super class): python thread class + """ + + def __init__(self, set_id="") -> SetItemsThread: + """Creates a new SetItemsThread instance to run async + returns: SetItemsThread instance + """ + threading.Thread.__init__(self) + self.set_id = set_id + self.listitems = [] + self.setinfo = {} + + def run(self): + if self.set_id and self.set_id != 0: + self.listitems, self.setinfo = tmdb.get_set_movies(self.set_id) diff --git a/script.extendedinfo/resources/lib/dialogs/dialogseasoninfo.py b/script.extendedinfo/resources/lib/dialogs/dialogseasoninfo.py new file mode 100644 index 000000000..f04715ad3 --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogseasoninfo.py @@ -0,0 +1,48 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +from resources.kutil131 import ActionHandler, addon + +from resources.kutil131 import imagetools, utils +from resources.lib import themoviedb as tmdb + +from .dialogvideoinfo import DialogVideoInfo + +ch = ActionHandler() + + +class DialogSeasonInfo(DialogVideoInfo): + TYPE = "Season" + LISTS = [(1000, "actors"), + (750, "crew"), + (2000, "episodes"), + (1150, "videos"), + (1250, "images"), + (1350, "backdrops")] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.tvshow_id = kwargs.get('id') + data = tmdb.extended_season_info(tvshow_id=self.tvshow_id, + season_number=kwargs.get('season')) + if not data: + return None + self.info, self.lists = data + if not self.info.get_info("dbid"): # need to add comparing for seasons + poster = utils.get_file(url=self.info.get_art("poster")) + self.info.set_art("poster", poster) + self.info.update_properties( + imagetools.blur(self.info.get_art("poster"))) + + def onInit(self): + self.get_youtube_vids("%s %s tv" % (self.info.get_info( + "tvshowtitle"), self.info.get_info('title'))) + super().onInit() + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def get_manage_options(self): + return [(addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")] diff --git a/script.extendedinfo/resources/lib/dialogs/dialogtvshowinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogtvshowinfo.py new file mode 100644 index 000000000..898ccef12 --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogtvshowinfo.py @@ -0,0 +1,154 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +import xbmc +import xbmcgui +from resources.kutil131 import ActionHandler, addon + +from resources.kutil131 import imagetools, utils +from resources.lib import themoviedb as tmdb +from resources.lib.windowmanager import wm + +from .dialogvideoinfo import DialogVideoInfo + +ID_LIST_SIMILAR = 150 +ID_LIST_SEASONS = 250 +ID_LIST_NETWORKS = 1450 +ID_LIST_STUDIOS = 550 +ID_LIST_CERTS = 650 +ID_LIST_CREW = 750 +ID_LIST_GENRES = 850 +ID_LIST_KEYWORDS = 950 +ID_LIST_ACTORS = 1000 +ID_LIST_VIDEOS = 1150 +ID_LIST_IMAGES = 1250 +ID_LIST_BACKDROPS = 1350 + +ID_BUTTON_BROWSE = 120 +ID_BUTTON_OPENLIST = 6002 +ID_BUTTON_RATED = 6006 + +ch = ActionHandler() + + +class DialogTVShowInfo(DialogVideoInfo): + TYPE = "TVShow" + TYPE_ALT = "tv" + LISTS = [(ID_LIST_SIMILAR, "similar"), + (ID_LIST_SEASONS, "seasons"), + (ID_LIST_NETWORKS, "networks"), + (ID_LIST_STUDIOS, "studios"), + (ID_LIST_CERTS, "certifications"), + (ID_LIST_CREW, "crew"), + (ID_LIST_GENRES, "genres"), + (ID_LIST_KEYWORDS, "keywords"), + (ID_LIST_ACTORS, "actors"), + (ID_LIST_VIDEOS, "videos"), + (ID_LIST_IMAGES, "images"), + (ID_LIST_BACKDROPS, "backdrops")] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + data = tmdb.extended_tvshow_info(tvshow_id=kwargs.get('tmdb_id'), + dbid=kwargs.get('dbid')) + if not data: + return None + self.info, self.lists, self.states = data + if not self.info.get_info("dbid"): + self.info.set_art("poster", utils.get_file( + self.info.get_art("poster"))) + self.info.update_properties( + imagetools.blur(self.info.get_art("poster"))) + + def onInit(self): + self.get_youtube_vids("%s tv" % (self.info.get_info("title"))) + super().onInit() + super().update_states() + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def set_buttons(self): + self.set_visible(ID_BUTTON_BROWSE, self.get_info("dbid")) + self.set_visible(ID_BUTTON_OPENLIST, self.logged_in) + self.set_visible(ID_BUTTON_RATED, True) + + @ch.click(ID_BUTTON_BROWSE) + def browse_tvshow(self, control_id): + self.exit() + xbmc.executebuiltin("Dialog.Close(all)") + xbmc.executebuiltin( + "ActivateWindow(videos,videodb://tvshows/titles/%s/)" % self.info.get_info("dbid")) + + @ch.click(ID_LIST_SEASONS) + def open_season_dialog(self, control_id): + info = self.FocusedItem(control_id).getVideoInfoTag() + wm.open_season_info(tvshow_id=self.info.get_property("id"), + season=info.getSeason(), + tvshow=self.info.get_info("title")) + + @ch.click(ID_LIST_STUDIOS) + def open_company_info(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_companies", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_KEYWORDS) + def open_keyword_info(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_keywords", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters) + + @ch.click(ID_LIST_GENRES) + def open_genre_info(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_genres", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters, + media_type="tv") + + @ch.click(ID_LIST_NETWORKS) + def open_network_info(self, control_id): + filters = [{"id": self.FocusedItem(control_id).getProperty("id"), + "type": "with_networks", + "label": self.FocusedItem(control_id).getLabel()}] + wm.open_video_list(filters=filters, + media_type="tv") + + def get_manage_options(self): + options = [] + title = self.info.get_info("tvshowtitle") + dbid = self.info.get_info("dbid") + #if not dbid: + # options += [(addon.LANG(32166), + # "RunPlugin(plugin://plugin.video.sickrage?action=addshow&show_name=%s)" % title)] + options.append( + (addon.LANG(1049), "Addon.OpenSettings(script.extendedinfo)")) + return options + + @ch.click(ID_BUTTON_OPENLIST) + def open_list(self, control_id): + index = xbmcgui.Dialog().select(heading=addon.LANG(32136), + list=[addon.LANG(32144), addon.LANG(32145)]) + if index == 0: + wm.open_video_list(media_type="tv", + mode="favorites") + elif index == 1: + wm.open_video_list(mode="rating", + media_type="tv") + + @ch.click(ID_BUTTON_RATED) + def open_rated_items(self, control_id): + wm.open_video_list(mode="rating", + media_type="tv") + + def update_states(self): + xbmc.sleep(2000) # delay because MovieDB takes some time to update + info = tmdb.get_tvshow(tvshow_id=self.info.get_property("id"), + cache_days=0) + self.states = info.get("account_states") + super().update_states() diff --git a/script.extendedinfo/resources/lib/dialogs/dialogvideoinfo.py b/script.extendedinfo/resources/lib/dialogs/dialogvideoinfo.py new file mode 100644 index 000000000..0ff42769a --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogvideoinfo.py @@ -0,0 +1,82 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +import xbmc +import xbmcgui +from resources.kutil131 import ActionHandler, addon + +from resources.kutil131 import utils +from resources.lib import themoviedb as tmdb + +from .dialogbaseinfo import DialogBaseInfo + +BUTTONS = {8, 9, 10, 6001, 6002, 6003, 6005, 6006} + +ID_BUTTON_PLOT = 132 +ID_BUTTON_MANAGE = 445 +ID_BUTTON_SETRATING = 6001 +ID_BUTTON_FAV = 6003 + +ch = ActionHandler() + + +class DialogVideoInfo(DialogBaseInfo): + """ + + Args: + DialogBaseInfo (_type_): _description_ + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def set_buttons(self): + for button_id in BUTTONS: + self.set_visible(button_id, False) + + @ch.click(ID_BUTTON_PLOT) + def show_plot(self, control_id): + xbmcgui.Dialog().textviewer(heading=addon.LANG(32037), + text=self.info.get_info("plot")) + + def get_manage_options(self): + return [] + + def get_identifier(self): + return self.info.get_property("id") + + @ch.click(ID_BUTTON_MANAGE) + def show_manage_dialog(self, control_id): + options = self.get_manage_options() + index = xbmcgui.Dialog().select(heading=addon.LANG(32133), + list=[i[0] for i in options]) + if index == -1: + return None + for item in options[index][1].split("||"): + xbmc.executebuiltin(item) + + @ch.click(ID_BUTTON_FAV) + def change_list_status(self, control_id): + tmdb.change_fav_status(media_id=self.info.get_property("id"), + media_type=self.TYPE_ALT, + status=str(not bool(self.states["favorite"])).lower()) + self.update_states() + + @ch.click(ID_BUTTON_SETRATING) + def set_rating_dialog(self, control_id): + preselect = int(self.states["rated"]["value"]) if ( + self.states and self.states.get("rated")) else -1 + rating = utils.input_userrating(preselect=preselect) + if rating == -1: + return None + if tmdb.set_rating(media_type=self.TYPE_ALT, + media_id=self.get_identifier(), + rating=rating, + dbid=self.info.get_info("dbid")): + self.setProperty("rated", str(rating) if rating > 0 else "") + self.update_states() diff --git a/script.extendedinfo/resources/lib/dialogs/dialogvideolist.py b/script.extendedinfo/resources/lib/dialogs/dialogvideolist.py new file mode 100644 index 000000000..78e7d3fd7 --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogvideolist.py @@ -0,0 +1,502 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +from __future__ import annotations + +import xbmc +import xbmcgui + +from resources.kutil131 import (ActionHandler, DialogBaseList, addon, busy, + confirmdialog, selectdialog, utils) +from resources.lib import themoviedb as tmdb +from resources.lib.windowmanager import wm + +ID_BUTTON_SORT = 5001 +ID_BUTTON_GENREFILTER = 5002 +ID_BUTTON_YEARFILTER = 5003 +ID_BUTTON_ORDER = 5004 +ID_BUTTON_CERTFILTER = 5006 +ID_BUTTON_ACTORFILTER = 5008 +ID_BUTTON_KEYWORDFILTER = 5009 +ID_BUTTON_COMPANYFILTER = 5010 +ID_BUTTON_RUNTIMEFILTER = 5011 +ID_BUTTON_VOTECOUNTFILTER = 5012 +ID_BUTTON_LANGUAGEFILTER = 5013 +ID_BUTTON_ACCOUNT = 7000 + +LANGUAGES = ['en', 'ja', 'ko', 'de', 'es', + 'ar', 'bn', 'bg', 'zh', 'da', + 'nl', 'fr', 'el', 'he', 'hu', + 'it', 'kn', 'no', 'pl', 'pt', + 'ru', 'sv', 'tr', 'uk'] + +ch = ActionHandler() + +include_adult: bool = addon.setting("include_adults").lower() + + +def get_window(window_type): + """Wrapper gets new DialogVideoList instance + + Args: + window_type (class instance): xbmc XML dialog window or + xbmc XML window objects + + Returns: + [DialogVideoList]: a new XML dialog or window + """ + + class DialogVideoList(DialogBaseList, window_type): + """Dialog Video List class + + Args: + DialogBaseList: Super class for dialog windows + window_type (kutils.windows class): Super class for Kodi xbmc.WindowXML + + """ + + TYPES = ["movie", "tv"] + + FILTERS = {"certification_country": addon.LANG(32153), + "certification": addon.LANG(32127), + "year": addon.LANG(562), + "with_genres": addon.LANG(135), + "with_people": addon.LANG(32156), + "with_companies": addon.LANG(20388), + "with_networks": addon.LANG(32152), + "with_keywords": addon.LANG(32114), + "first_air_date": addon.LANG(20416), + "with_runtime": xbmc.getLocalizedString(2050), + "primary_release_date": addon.LANG(345), + "vote_count": addon.LANG(32111), + "with_original_language": xbmc.getLocalizedString(308)} + + TRANSLATIONS = {"movie": addon.LANG(20338), + "tv": addon.LANG(20364), + "person": addon.LANG(32156)} + + SORTS = {"movie": {"popularity": addon.LANG(32110), + "release_date": addon.LANG(172), + "revenue": addon.LANG(32108), + # "Release Date": "primary_release_date", + "original_title": addon.LANG(20376), + "vote_average": addon.LANG(32112), + "vote_count": addon.LANG(32111)}, + "tv": {"popularity": addon.LANG(32110), + "first_air_date": addon.LANG(20416), + "vote_average": addon.LANG(32112)}, + "favorites": {"created_at": addon.LANG(32157)}, + "list": {"created_at": addon.LANG(32157)}, + "rating": {"created_at": addon.LANG(32157)}} + + LABEL2 = {"popularity": lambda x: x.get_property("popularity"), + "release_date": lambda x: x.get_info("premiered"), + "revenue": lambda x: x.get_info("genre"), + "vote_average": lambda x: x.get_info("rating"), + "vote_count": lambda x: "{} {}".format(x.get_info("votes"), addon.LANG(32082)), + "first_air_date": lambda x: x.get_info("premiered"), + "created_at": lambda x: x.get_property("created_at"), + "original_title": lambda x: x.get_info("originaltitle")} + + @busy.set_busy + def __init__(self, *args, **kwargs): + self.type = kwargs.get('type', "movie") + self.list_id = kwargs.get("list_id", False) + self.logged_in = tmdb.Login.check_login() + super().__init__(*args, **kwargs) + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def onAction(self, action): + super().onAction(action) + ch.serve_action(action, self.getFocusId(), self) + + def update_ui(self): + super().update_ui() + self.getControl(ID_BUTTON_CERTFILTER).setVisible(self.type != "tv") + self.getControl(ID_BUTTON_ACTORFILTER).setVisible( + self.type != "tv") + self.getControl(ID_BUTTON_KEYWORDFILTER).setVisible( + self.type != "tv") + self.getControl(ID_BUTTON_COMPANYFILTER).setVisible( + self.type != "tv") + + @ch.context("tvshow") + @ch.context("movie") + def context_menu(self, control_id): + item_id = self.FocusedItem(control_id).getProperty("id") + media_type = self.FocusedItem( + control_id).getVideoInfoTag().getMediaType() + listitems = [addon.LANG(32169)] if media_type == "tvshow" else [ + addon.LANG(32113)] + if self.logged_in: + listitems += [addon.LANG(14076)] + if not self.type == "tv": + listitems += [addon.LANG(32107)] + if self.mode == "list": + listitems += [addon.LANG(32035)] + index = xbmcgui.Dialog().contextmenu(list=listitems) + if index == 0: + # HACK until we can get userrating from listitem via python + rating = utils.get_infolabel("listitem.userrating") + rating = utils.input_userrating( + preselect=int(rating) if rating.isdigit() else -1) + if rating == -1: + return None + if tmdb.set_rating(media_type="tv" if media_type == "tvshow" else "movie", + media_id=item_id, + rating=rating, + dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()): + xbmc.sleep(2000) + self.update(force_update=True) + self.setCurrentListPosition(self.position) + elif index == 1: + tmdb.change_fav_status(media_id=item_id, + media_type=self.type, + status="true") + elif index == 2: + self.list_dialog(item_id) + elif index == 3: + tmdb.change_list_status(list_id=self.list_id, + movie_id=item_id, + status=False) + self.update(force_update=True) + self.setCurrentListPosition(self.position) + + def list_dialog(self, movie_id): + busy.show_busy() + listitems = [addon.LANG(32139)] + account_lists = tmdb.get_account_lists() + listitems += ["%s (%i)" % (i["name"], i["item_count"]) + for i in account_lists] + listitems.append(addon.LANG(32138)) + busy.hide_busy() + index = xbmcgui.Dialog().select(heading=addon.LANG(32136), + list=listitems) + if index == 0: + listname = xbmcgui.Dialog().input(heading=addon.LANG(32137), + type=xbmcgui.INPUT_ALPHANUM) + if listname: + xbmc.sleep(1000) + tmdb.change_list_status(list_id=tmdb.create_list(listname), + movie_id=movie_id, + status=True) + elif index == len(listitems) - 1: + tmdb.remove_list_dialog(tmdb.handle_lists(account_lists)) + elif index > 0: + tmdb.change_list_status(list_id=account_lists[index - 1]["id"], + movie_id=movie_id, + status=True) + + @property + def sort_key(self): + return self.mode if self.mode in ["favorites", "rating", "list"] else self.type + + @property + def default_sort(self): + return "created_at" if self.mode in ["favorites", "rating", "list"] else "popularity" + + @ch.click(ID_BUTTON_SORT) + def get_sort_type(self, control_id): + if not self.choose_sort_method(self.sort_key): + return None + if self.sort == "vote_average": + self.add_filter(key="vote_count.gte", + value="10", + label="10", + reset=False) + self.update() + + def add_filter(self, **kwargs): + key = kwargs["key"].replace(".gte", "").replace(".lte", "") + kwargs["typelabel"] = self.FILTERS[key] + if kwargs["key"].endswith(".lte"): + kwargs["label"] = "< %s" % kwargs["label"] + if kwargs["key"].endswith(".gte"): + kwargs["label"] = "> %s" % kwargs["label"] + super().add_filter(force_overwrite=kwargs["key"].endswith((".gte", ".lte")), + **kwargs) + + @ch.click(ID_BUTTON_ORDER) + def toggle_order(self, control_id): + self.order = "desc" if self.order == "asc" else "asc" + self.update() + + @ch.click(ID_BUTTON_ACCOUNT) + def open_account_menu(self, control_id): + if self.type == "tv": + listitems = [addon.LANG(32145)] + if self.logged_in: + listitems.append(addon.LANG(32144)) + else: + listitems = [addon.LANG(32135)] + if self.logged_in: + listitems.append(addon.LANG(32134)) + busy.show_busy() + if self.logged_in: + account_lists = tmdb.get_account_lists() + listitems += ["%s (%i)" % (i["name"], i["item_count"]) + for i in account_lists] + busy.hide_busy() + index = xbmcgui.Dialog().select(heading=addon.LANG(32136), + list=listitems) + if index == -1: + pass + elif index == 0: + self.set_sort("created_at") + self.filters = [] + self.reset("rating") + elif index == 1: + self.set_sort("created_at") + self.filters = [] + self.reset("favorites") + else: + self.close() + wm.open_video_list(filters=[], + mode="list", + list_id=account_lists[index - 2]["id"], + filter_label=account_lists[index - 2]["name"]) + + @ch.click(ID_BUTTON_GENREFILTER) + def set_genre_filter(self, control_id): + params = {"language": addon.setting("LanguageID")} + response = tmdb.get_data(url="genre/%s/list" % (self.type), + params=params, + cache_days=100) + selected = [i["id"] + for i in self.filters if i["type"] == "with_genres"] + ids = [item["id"] for item in response["genres"]] + labels = [item["name"] for item in response["genres"]] + preselect = [ids.index(int(i)) + for i in selected[0].split(",")] if selected else [] + indexes = xbmcgui.Dialog().multiselect(heading=addon.LANG(32151), + options=labels, + preselect=preselect) + if indexes is None: + return None + self.filters = [ + i for i in self.filters if i["type"] != "with_genres"] + for i in indexes: + self.add_filter(key="with_genres", + value=ids[i], + label=labels[i], + reset=False) + self.reset() + + @ch.click(ID_BUTTON_VOTECOUNTFILTER) + def set_vote_count_filter(self, control_id): + ret = True + if not self.type == "tv": + ret = confirmdialog.open(header=addon.LANG(32151), + text=addon.LANG(32106), + nolabel=addon.LANG(32150), + yeslabel=addon.LANG(32149), + path=addon.PATH) + if ret == -1: + return None + result = xbmcgui.Dialog().input(heading=addon.LANG(32111), + type=xbmcgui.INPUT_NUMERIC) + if result: + self.add_filter(key="vote_count.lte" if ret == 1 else "vote_count.gte", + value=result, + label=result) + + @ch.click(ID_BUTTON_YEARFILTER) + def set_year_filter(self, control_id): + ret = confirmdialog.open(header=addon.LANG(32151), + text=addon.LANG(32106), + nolabel=addon.LANG(32150), + yeslabel=addon.LANG(32149), + path=addon.PATH) + if ret == -1: + return None + result = xbmcgui.Dialog().input(heading=addon.LANG(345), + type=xbmcgui.INPUT_NUMERIC) + if not result: + return None + value = "{}-12-31" if ret == 1 else "{}-01-01" + key = "first_air_date" if self.type == "tv" else "primary_release_date" + self.add_filter(key="%s.%s" % (key, "lte" if ret == 1 else "gte"), + value=value.format(result), + label=result) + + @ch.click(ID_BUTTON_RUNTIMEFILTER) + def set_runtime_filter(self, control_id): + ret = confirmdialog.open(header=addon.LANG(32151), + text=addon.LANG(32106), + nolabel=addon.LANG(32150), + yeslabel=addon.LANG(32149), + path=addon.PATH) + if ret == -1: + return None + result = xbmcgui.Dialog().input(heading=xbmc.getLocalizedString(2050), + type=xbmcgui.INPUT_NUMERIC) + if not result: + return None + self.add_filter(key="with_runtime.%s" % ("lte" if ret == 1 else "gte"), + value=result, + label="{} min".format(result)) + + @ch.click(ID_BUTTON_ACTORFILTER) + def set_actor_filter(self, control_id): + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if not result or result == -1: + return None + response = tmdb.get_person_info(result) + if not response: + return None + self.add_filter(key="with_people", + value=response["id"], + label=response["name"]) + + @ch.info("movie") + @ch.click_by_type("movie") + def open_movie(self, control_id): + wm.open_movie_info(movie_id=self.FocusedItem(control_id).getProperty("id"), + dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) + + @ch.info("tvshow") + @ch.click_by_type("tvshow") + def open_tvshow(self, control_id): + wm.open_tvshow_info(tmdb_id=self.FocusedItem(control_id).getProperty("id"), + dbid=self.FocusedItem(control_id).getVideoInfoTag().getDbId()) + + @ch.info("artist") + @ch.click_by_type("artist") + def open_media(self, control_id): + wm.open_actor_info(actor_id=self.FocusedItem( + control_id).getProperty("id")) + + @ch.click(ID_BUTTON_COMPANYFILTER) + def set_company_filter(self, control_id): + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if not result: + return None + items = tmdb.search_companies(result) + if len(items) > 1: + index = selectdialog.open(header=addon.LANG(32151), + listitems=items) + if index > -1: + item = items[index] + elif items: + item = items[0] + else: + utils.notify("No company found") + self.add_filter(key="with_companies", + value=item.get_property("id"), + label=item.get_label()) + + @ch.click(ID_BUTTON_KEYWORDFILTER) + def set_keyword_filter(self, control_id): + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if not result or result == -1: + return None + keywords = tmdb.get_keywords(result) + if not keywords: + return None + if len(keywords) > 1: + index = xbmcgui.Dialog().select(heading=addon.LANG(32114), + list=[item["name"] for item in keywords]) + keyword = keywords[index] if index > -1 else None + if not keyword: + return None + else: + keyword = keywords[0] + self.add_filter(key="with_keywords", + value=keyword["id"], + label=keyword["name"]) + + @ch.click(ID_BUTTON_CERTFILTER) + def set_certification_filter(self, control_id): + response = tmdb.get_certification_list(self.type) + countries = [key for key in list(response.keys())] + index = xbmcgui.Dialog().select(heading=addon.LANG(21879), + list=countries) + if index == -1: + return None + country = countries[index] + certs = ["%s - %s" % (i["certification"], i["meaning"]) + for i in response[country]] + index = xbmcgui.Dialog().select(heading=addon.LANG(32151), + list=certs) + if index == -1: + return None + cert = certs[index].split(" - ")[0] + self.add_filter(key="certification_country", + value=country, + label=country, + reset=False) + self.add_filter(key="certification", + value=cert, + label=cert) + + @ch.click(ID_BUTTON_LANGUAGEFILTER) + def set_language_filter(self, control_id): + index = xbmcgui.Dialog().select(heading=f'ISO-639-1 {xbmc.getLocalizedString(304)}', + list=LANGUAGES) + if index == -1: + return None + language = LANGUAGES[index] + self.add_filter(key="with_original_language", + value=language, + label=language) + + def fetch_data(self, force=False): # TODO: rewrite + sort_by = self.sort + "." + self.order + temp = "tv" if self.type == "tv" else "movies" + if self.mode == "search": + self.filter_label = addon.LANG( + 32146) % self.search_str if self.search_str else "" + return tmdb.multi_search(search_str=self.search_str, + page=self.page, + cache_days=0 if force else 2) + if self.mode == "list": + return tmdb.get_list_movies(list_id=self.list_id, + force=force) + if self.mode == "favorites": + self.filter_label = addon.LANG( + 32144) if self.type == "tv" else addon.LANG(32134) + return tmdb.get_fav_items(media_type=temp, + sort_by=sort_by, + page=self.page) + elif self.mode == "rating": + self.filter_label = addon.LANG( + 32145) if self.type == "tv" else addon.LANG(32135) + return tmdb.get_rated_media_items(media_type=temp, + sort_by=sort_by, + page=self.page, + cache_days=0) + else: #self.mode == "filter" + self.set_filter_label() + params = {"sort_by": sort_by, + "language": addon.setting("LanguageID"), + "page": self.page, + "include_adult": include_adult} + filters = {item["type"]: item["id"] for item in self.filters} + response = tmdb.get_data(url=f"discover/{self.type}", + params=utils.merge_dicts( + params, filters), + cache_days=0 if force else 2) + + if not response["results"]: + utils.notify(addon.LANG(284)) + return None + if self.type == "movie": + itemlist = tmdb.handle_movies(results=response["results"], + local_first=False, + sortkey=None) + else: + itemlist = tmdb.handle_tvshows(results=response["results"], + local_first=False, + sortkey=None) + itemlist.set_totals(response["total_results"]) + itemlist.set_total_pages(response["total_pages"]) + return itemlist + + return DialogVideoList diff --git a/script.extendedinfo/resources/lib/dialogs/dialogyoutubelist.py b/script.extendedinfo/resources/lib/dialogs/dialogyoutubelist.py new file mode 100644 index 000000000..cc3206ef9 --- /dev/null +++ b/script.extendedinfo/resources/lib/dialogs/dialogyoutubelist.py @@ -0,0 +1,236 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +import datetime + +import xbmcgui +from resources.kutil131 import ActionHandler, DialogBaseList, addon, busy, windows + +from resources.kutil131 import utils, youtube +from resources.lib.windowmanager import wm + +ch = ActionHandler() + +ID_BUTTON_SORTTYPE = 5001 +ID_BUTTON_PUBLISHEDFILTER = 5002 +ID_BUTTON_LANGUAGEFILTER = 5003 +ID_BUTTON_DIMENSIONFILTER = 5006 +ID_BUTTON_DURATIONFILTER = 5008 +ID_BUTTON_CAPTIONFILTER = 5009 +ID_BUTTON_DEFINITIONFILTER = 5012 +ID_BUTTON_TYPEFILTER = 5013 + + +def get_window(window_type): + + class DialogYoutubeList(DialogBaseList, window_type): + + TYPES = ["video", "playlist", "channel"] + + FILTERS = {"channelId": addon.LANG(19029), + "publishedAfter": addon.LANG(172), + "regionCode": addon.LANG(248), + "videoDimension": addon.LANG(32057), + "videoDuration": addon.LANG(180), + "videoCaption": addon.LANG(287), + "videoDefinition": addon.LANG(32058), + "videoType": "Type", + "relatedToVideoId": addon.LANG(32058)} + + TRANSLATIONS = {"video": addon.LANG(157), + "playlist": addon.LANG(559), + "channel": addon.LANG(19029)} + + SORTS = {"video": {"date": addon.LANG(552), + "rating": addon.LANG(563), + "relevance": addon.LANG(32060), + "title": addon.LANG(369), + "viewCount": addon.LANG(567)}, + "playlist": {"date": addon.LANG(552), + "rating": addon.LANG(563), + "relevance": addon.LANG(32060), + "title": addon.LANG(369), + "videoCount": addon.LANG(32068), + "viewCount": addon.LANG(567)}, + "channel": {"date": addon.LANG(552), + "rating": addon.LANG(563), + "relevance": addon.LANG(32060), + "title": addon.LANG(369), + "videoCount": addon.LANG(32068), + "viewCount": addon.LANG(567)}} + + LABEL2 = {"date": lambda x: x.get_info("date"), + "relevance": lambda x: x.get_property("relevance"), + "title": lambda x: x.get_info("title"), + "viewCount": lambda x: x.get_property("viewCount"), + "videoCount": lambda x: x.get_property("videoCount"), + "rating": lambda x: x.get_info("rating")} + + @busy.set_busy + def __init__(self, *args, **kwargs): + self.type = kwargs.get('type', "video") + super().__init__(*args, **kwargs) + + def onClick(self, control_id): + super().onClick(control_id) + ch.serve(control_id, self) + + def onAction(self, action): + super().onAction(action) + ch.serve_action(action, self.getFocusId(), self) + + @ch.click_by_type("video") + def main_list_click(self, control_id): + listitem = self.FocusedItem(control_id) + youtube_id = listitem.getProperty("youtube_id") + media_type = listitem.getProperty("type") + if media_type == "channel": + filter_ = [{"id": youtube_id, + "type": "channelId", + "label": listitem.getLabel()}] + wm.open_youtube_list(filters=filter_) + else: + wm.play_youtube_video(youtube_id=youtube_id, + listitem=listitem) + + @ch.click(ID_BUTTON_PUBLISHEDFILTER) + def set_published_filter(self, control_id): + options = [(1, addon.LANG(32062)), + (7, addon.LANG(32063)), + (31, addon.LANG(32064)), + (365, addon.LANG(32065)), + ("custom", addon.LANG(636))] + deltas = [i[0] for i in options] + labels = [i[1] for i in options] + index = xbmcgui.Dialog().select(heading=addon.LANG(32151), + list=labels) + if index == -1: + return None + delta = deltas[index] + if delta == "custom": + delta = xbmcgui.Dialog().input(heading=addon.LANG(32067), + type=xbmcgui.INPUT_NUMERIC) + if not delta: + return None + d = datetime.datetime.now() - datetime.timedelta(int(delta)) + self.add_filter(key="publishedAfter", + value=d.isoformat('T')[:-7] + "Z", + label=labels[index]) + + @ch.click(ID_BUTTON_LANGUAGEFILTER) + def set_language_filter(self, control_id): + options = [("en", "en"), + ("de", "de"), + ("fr", "fr")] + self.choose_filter("regionCode", 32151, options) + + @ch.click(ID_BUTTON_DIMENSIONFILTER) + def set_dimension_filter(self, control_id): + options = [("2d", "2D"), + ("3d", "3D"), + ("any", addon.LANG(593))] + self.choose_filter("videoDimension", 32151, options) + + @ch.click(ID_BUTTON_DURATIONFILTER) + def set_duration_filter(self, control_id): + options = [("long", addon.LANG(33013)), + ("medium", addon.LANG(601)), + ("short", addon.LANG(33012)), + ("any", addon.LANG(593))] + self.choose_filter("videoDuration", 32151, options) + + @ch.click(ID_BUTTON_CAPTIONFILTER) + def set_caption_filter(self, control_id): + options = [("closedCaption", addon.LANG(107)), + ("none", addon.LANG(106)), + ("any", addon.LANG(593))] + self.choose_filter("videoCaption", 287, options) + + @ch.click(ID_BUTTON_DEFINITIONFILTER) + def set_definition_filter(self, control_id): + options = [("high", addon.LANG(419)), + ("standard", addon.LANG(602)), + ("any", addon.LANG(593))] + self.choose_filter("videoDefinition", 169, options) + + @ch.click(ID_BUTTON_TYPEFILTER) + def set_type_filter(self, control_id): + options = [("movie", addon.LANG(20338)), + ("episode", addon.LANG(20359)), + ("any", addon.LANG(593))] + self.choose_filter("videoType", 32151, options) + + @ch.click(ID_BUTTON_SORTTYPE) + def get_sort_type(self, control_id): + if not self.choose_sort_method(self.type): + return None + self.update() + + @ch.context("video") + def context_menu(self, control_id): + listitem = self.FocusedItem(control_id) + if self.type == "video": + more_vids = "{} [B]{}[/B]".format(addon.LANG(32081), + listitem.getProperty("channel_title")) + index = xbmcgui.Dialog().contextmenu( + list=[addon.LANG(32069), more_vids]) + if index < 0: + return None + elif index == 0: + filter_ = [{"id": listitem.getProperty("youtube_id"), + "type": "relatedToVideoId", + "label": listitem.getLabel()}] + wm.open_youtube_list(filters=filter_) + elif index == 1: + filter_ = [{"id": listitem.getProperty("channel_id"), + "type": "channelId", + "label": listitem.getProperty("channel_title")}] + wm.open_youtube_list(filters=filter_) + + def update_ui(self): + is_video = self.type == "video" + self.getControl(ID_BUTTON_DIMENSIONFILTER).setVisible(is_video) + self.getControl(ID_BUTTON_DURATIONFILTER).setVisible(is_video) + self.getControl(ID_BUTTON_CAPTIONFILTER).setVisible(is_video) + self.getControl(ID_BUTTON_DEFINITIONFILTER).setVisible(is_video) + super().update_ui() + + @property + def default_sort(self): + return "relevance" + + def add_filter(self, **kwargs): + kwargs["typelabel"] = self.FILTERS[kwargs["key"]] + super().add_filter(force_overwrite=True, + **kwargs) + + def fetch_data(self, force=False): + self.set_filter_label() + if self.search_str: + self.filter_label = addon.LANG(32146) % ( + self.search_str) + " " + self.filter_label + user_key = addon.setting("Youtube API Key") + return youtube.search(search_str=self.search_str, + orderby=self.sort, + extended=True, + filters={item["type"]: item["id"] + for item in self.filters}, + media_type=self.type, + page=self.page_token, + api_key=user_key) + + return DialogYoutubeList + + +def open(self, search_str="", filters=None, sort="relevance", filter_label="", media_type="video"): + """ + open video list, deal with window stack + """ + YouTube = get_window(windows.DialogXML) + dialog = YouTube('script-%s-YoutubeList.xml' % addon.NAME, addon.PATH, + search_str=search_str, + filters=[] if not filters else filters, + filter_label=filter_label, + type=media_type) + return dialog diff --git a/script.extendedinfo/resources/lib/lastfm.py b/script.extendedinfo/resources/lib/lastfm.py new file mode 100644 index 000000000..4f268540a --- /dev/null +++ b/script.extendedinfo/resources/lib/lastfm.py @@ -0,0 +1,206 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details +"""Uses LastFM API to query data from LastFM. + +The get_* functions are called to query LastFM API. + +""" + +import re +import urllib.error +import urllib.parse +import urllib.request +from typing import Optional + +from resources.kutil131 import ItemList + +from resources.kutil131 import utils + +LAST_FM_API_KEY = 'd942dd5ca4c9ee5bd821df58cf8130d4' +GOOGLE_MAPS_KEY = 'AIzaSyBESfDvQgWtWLkNiOYXdrA9aU-2hv_eprY' +BASE_URL = 'http://ws.audioscrobbler.com/2.0/?' + + +def _handle_albums(results: dict) -> ItemList: + """Converts TADB query results to kutils131 ItemList + + Args: + results (dict): TADB albums for an artist + + Returns: + ItemList: a kutils131 ItemList od dicts + """ + albums = ItemList(content_type="albums") + if not results: + return albums + if 'topalbums' in results and "album" in results['topalbums']: + for album in results['topalbums']['album']: + albums.append({'artist': album['artist']['name'], + 'mbid': album.get('mbid', ""), + 'mediatype': "album", + 'thumb': album['image'][-1]['#text'], + 'label': f"{album['artist']['name']} - {album['name']}", + 'title': album['name']}) + albums.append(album) + return albums + + +def _handle_artists(results) -> ItemList: + """Converts TADB artist query to kutils131 ItemList + + Args: + results (_type_): _description_ + + Returns: + ItemList: a kutils131 ItemList of artist info as dicts + """ + artists = ItemList(content_type="artists") + if not results: + return artists + for artist in results['artist']: + if 'name' not in artist: + continue + artist = {'title': artist['name'], + 'label': artist['name'], + 'mediatype': "artist", + 'mbid': artist.get('mbid'), + 'thumb': artist['image'][-1]['#text'], + 'Listeners': format(int(artist.get('listeners', 0)), ",d")} + artists.append(artist) + return artists + + +def get_top_artists() -> ItemList: + """Queries LastFM api chart.getTopArtists method for top 100 artists + + Returns: + ItemList: a kutils131 object that wraps a list of artist + info dicts + """ + results: Optional[dict] = get_data(method="chart.getTopArtists", + params={"limit": "100"}) + return _handle_artists(results['artists']) + + +def get_artist_albums(artist_mbid: str) -> ItemList: + """Queries LastFM api artist.getTopAlbums method for an artist + + Gets 50 albums with title, mbid, and cover image + + Args: + artist_mbid (str): The musicbrainz id for the artist + + Returns: + ItemList: a kutils131object that wraps a list of albums + info dicts + """ + if not artist_mbid: + return ItemList(content_type="albums") + results = get_data(method="artist.getTopAlbums", + params={"mbid": artist_mbid}) + return _handle_albums(results) + + +def get_similar_artists(artist_mbid: str) -> ItemList: + """Queries LastFM api artist.getsimilar for artists + + Gets name, mbid, and thumb image of similar artists + + Args: + artist_mbid (str): The musicbrainz id for the artist + + Returns: + ItemList: a kutils131 object that wraps a list of artists info dicts + """ + if not artist_mbid: + return ItemList(content_type="artists") + params = {"mbid": artist_mbid, + "limit": "400"} + results = get_data(method="artist.getSimilar", + params=params) + if results and "similarartists" in results: + return _handle_artists(results['similarartists']) + + +def get_track_info(artist_name="", track="") -> dict: + """ Queries LastFM api + + Args: + artist_name (str, optional): The artist name. Defaults to "". + track (str, optional): The track name. Defaults to "". + + Returns: + dict: LastFM info including scrobles of a song. + """ + if not artist_name or not track: + return {} + params = {"artist": artist_name, + "track": track} + results: Optional[dict] = get_data(method="track.getInfo", + params=params) + if not results: + return {} + summary = results['track']['wiki']['summary'] if "wiki" in results['track'] else "" + return {'playcount': str(results['track']['playcount']), + 'thumb': str(results['track']['playcount']), + 'summary': clean_text(summary)} + + +def get_data(method: str, params=None, cache_days=0.5) -> dict: + """helper function runs query including using local cache + + Args: + method (str): LastFM api method + params (dict, optional): LastFM method parameters. Defaults to None. + cache_days (float, optional): Days to use cache/query. Defaults to 0.5. + + Returns: + dict: The json.loads results from the query + """ + params = params if params else {} + params["method"] = method + params["api_key"] = LAST_FM_API_KEY + params["format"] = "json" + params = {k: str(v) for k, v in params.items() if v} + url = f"{BASE_URL}{urllib.parse.urlencode(params)}" + return utils.get_JSON_response(url=url, + cache_days=cache_days, + folder="LastFM") + + +def clean_text(text) -> str: + """Helper function to unescape chars + + Args: + text (str): text string to unescape + + Returns: + str: text string + """ + if not text: + return "" + text = re.sub( + '(From Wikipedia, the free encyclopedia)|(Description above from the Wikipedia.*?Wikipedia)', '', text) + text = re.sub('<(.|\n|\r)*?>', '', text) + text = text.replace('
', '[CR]') + text = text.replace('', '[I]').replace('', '[/I]') + text = text.replace('&', '&') + text = text.replace('>', '>').replace('<', '<') + text = text.replace(''', "'").replace('"', '"') + text = re.sub("\n\\.$", "", text) + text = text.replace( + 'User-contributed text is available under the Creative Commons By-SA License and may also be available under the GNU FDL.', '') + removals = {'\u200b', " ", "\n"} + while text: + s = text[0] + e = text[-1] + if s in removals: + text = text[1:] + elif e in removals: + text = text[:-1] + elif s.startswith(".") and not s.startswith(".."): + text = text[1:] + else: + break + return text.strip() diff --git a/script.extendedinfo/resources/lib/omdb.py b/script.extendedinfo/resources/lib/omdb.py index bbc2a2fab..d930cb15c 100644 --- a/script.extendedinfo/resources/lib/omdb.py +++ b/script.extendedinfo/resources/lib/omdb.py @@ -1,5 +1,3 @@ -# -*- coding: utf8 -*- - # Copyright (C) 2015 - Philipp Temminghoff # Modifications copyright (C) 2022 - Scott Smart # This program is Free Software see LICENSE file for details @@ -9,24 +7,26 @@ """ -from typing import Optional +from __future__ import annotations + +from resources.kutil131 import addon -from kutils import addon, utils +from resources.kutil131 import utils BASE_URL = "http://www.omdbapi.com/?tomatoes=true&plot=full&r=json&" -def get_movie_info(imdb_id: str) -> Optional[dict]: +def get_movie_info(imdb_id: str) -> dict | None: """gets tomato data from OMDb Args: imdb_id (str): imbd id Returns: - Optional[dict]: Json.loads response from OMDb + dict | None: Json.loads response from OMDb or None if not available """ omdb_key: str = addon.setting('OMDb API Key') - url = 'apikey={0}&i={1}'.format(omdb_key, imdb_id) + url = f'apikey={omdb_key}&i={imdb_id}' results = utils.get_JSON_response(BASE_URL + url, 20, "OMDB") if not results: return None diff --git a/script.extendedinfo/resources/lib/process.py b/script.extendedinfo/resources/lib/process.py index 83b0601c6..bbcdee822 100644 --- a/script.extendedinfo/resources/lib/process.py +++ b/script.extendedinfo/resources/lib/process.py @@ -1,43 +1,45 @@ -# -*- coding: utf8 -*- - # Copyright (C) 2015 - Philipp Temminghoff # Modifications copyright (C) 2022 - Scott Smart # This program is Free Software see LICENSE file for details """When addon is called with RunScript, executes required action +When called as plugin, sets listitems to container -Action to run is passed in the call, along with any associated parameters +Public functions: + start_info_actions(info, params): takes in invocation info action + and optional param key/value pairs and exexutes the info action """ +from __future__ import annotations + import os import shutil import time -from typing import Dict import xbmc import xbmcgui import xbmcplugin +from resources.kutil131 import addon, busy, kodijson -from kutils import addon, busy, favs, kodijson, local_db, utils, youtube -from resources.lib import LastFM -from resources.lib import TheAudioDB as AudioDB -from resources.lib import TheMovieDB as tmdb -from resources.lib import Trakt +from resources.kutil131 import favs, local_db, utils, youtube +from resources.lib import lastfm +from resources.lib import theaudiodb as AudioDB +from resources.lib import themoviedb as tmdb +from resources.lib import trakt +from resources.lib.windowmanager import wm -from .WindowManager import wm - -def start_info_actions(info: str, params: Dict[str, str]) -> list: +def start_info_actions(info: str, params: dict[str, str]): """executes an action from infos (info= list) using any params See README for list of possible actions Args: info (str): one of a defined infos list of possible actions - params (Dict[str,str]): Optional parameters for the action + params (dict[str,str]): Optional parameters for the action Returns: - [type]: [description] + [ItemList]: a kodi utils ItemList of VideoItems/Music """ if "artistname" in params: params["artistname"] = params.get( @@ -45,7 +47,7 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: if not params.get("artist_mbid"): params["artist_mbid"] = utils.fetch_musicbrainz_id( params["artistname"]) - utils.log(f'start_info_actions: {info}') + utils.log(f'process start_info_actions info: {info} params:') utils.pp(params) if "prefix" in params and not params["prefix"].endswith('.'): params["prefix"] = params["prefix"] + '.' @@ -54,14 +56,14 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: if info == 'discography': discography = AudioDB.get_artist_discography(params["artistname"]) if not discography: - discography = LastFM.get_artist_albums(params.get("artist_mbid")) + discography = lastfm.get_artist_albums(params.get("artist_mbid")) return discography elif info == 'mostlovedtracks': return AudioDB.get_most_loved_tracks(params["artistname"]) elif info == 'trackdetails': return AudioDB.get_track_details(params.get("id", "")) elif info == 'topartists': - return LastFM.get_top_artists() + return lastfm.get_top_artists() # The MovieDB elif info == 'incinemamovies': return tmdb.get_movies("now_playing") @@ -79,9 +81,10 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: account_lists = tmdb.handle_lists(tmdb.get_account_lists()) for item in account_lists: item.set_property("directory", True) + item.set_folder(True) return account_lists elif info == 'listmovies': - return tmdb.get_movies_from_list(params["id"]) + return tmdb.get_movies_from_list(params["id"]) if params.get('id') else [] elif info == 'airingtodaytvshows': return tmdb.get_tvshows("airing_today") elif info == 'onairtvshows': @@ -181,7 +184,7 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: movie_id = local_db.get_imdb_id("movie", params["dbid"]) else: movie_id = params["id"] - return Trakt.get_similar("movie", movie_id) + return trakt.get_similar("movie", movie_id) elif info == 'traktsimilartvshows': if params.get("id") or params.get("dbid"): if params.get("dbid"): @@ -193,56 +196,56 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: dbid=params["dbid"]) else: tvshow_id = params["id"] - return Trakt.get_similar("show", tvshow_id) + return trakt.get_similar("show", tvshow_id) elif info == 'airingepisodes': - return Trakt.get_episodes("shows") + return trakt.get_episodes("shows") elif info == 'premiereepisodes': - return Trakt.get_episodes("premieres") + return trakt.get_episodes("premieres") elif info == 'trendingshows': - return Trakt.get_shows("trending") + return trakt.get_shows("trending") elif info == 'popularshows': - return Trakt.get_shows("popular") + return trakt.get_shows("popular") elif info == 'anticipatedshows': - return Trakt.get_shows("anticipated") + return trakt.get_shows("anticipated") elif info == 'mostcollectedshows': - return Trakt.get_shows_from_time("collected") + return trakt.get_shows_from_time("collected") elif info == 'mostplayedshows': - return Trakt.get_shows_from_time("played") + return trakt.get_shows_from_time("played") elif info == 'mostwatchedshows': - return Trakt.get_shows_from_time("watched") + return trakt.get_shows_from_time("watched") elif info == 'trendingmovies': - return Trakt.get_movies("trending") + return trakt.get_movies("trending") elif info == 'traktpopularmovies': - return Trakt.get_movies("popular") + return trakt.get_movies("popular") elif info == 'mostplayedmovies': - return Trakt.get_movies_from_time("played") + return trakt.get_movies_from_time("played") elif info == 'mostwatchedmovies': - return Trakt.get_movies_from_time("watched") + return trakt.get_movies_from_time("watched") elif info == 'mostcollectedmovies': - return Trakt.get_movies_from_time("collected") + return trakt.get_movies_from_time("collected") elif info == 'mostanticipatedmovies': - return Trakt.get_movies("anticipated") + return trakt.get_movies("anticipated") elif info == 'traktboxofficemovies': - return Trakt.get_movies("boxoffice") + return trakt.get_movies("boxoffice") elif info == 'similarartistsinlibrary': return local_db.get_similar_artists(params.get("artist_mbid")) # LastFM elif info == 'trackinfo': - addon.clear_global('%sSummary' % params.get("prefix", "")) + addon.clear_global(f'{params.get("prefix", "")}Summary') if params["artistname"] and params["trackname"]: - track_info = LastFM.get_track_info(artist_name=params["artistname"], + track_info = lastfm.get_track_info(artist_name=params["artistname"], track=params["trackname"]) - addon.set_global('%sSummary' % params.get( - "prefix", ""), track_info["summary"]) + addon.set_global(f'{params.get("prefix", "")}Summary', + track_info["summary"]) # Bands in town API no longer provides event access # elif info == 'topartistsnearevents': # artists = local_db.get_artists() - # from . import BandsInTown - # return BandsInTown.get_near_events(artists[0:49]) + # from . import bandsintown + # return bandsintown.get_near_events(artists[0:49]) # Youtube elif info == 'youtubesearchvideos': - addon.set_global('%sSearchValue' % params.get( - "prefix", ""), params.get("id", "")) + addon.set_global(f'{params.get("prefix", "")}SearchValue', + params.get("id", "")) user_key = addon.setting("Youtube API Key") if params.get("id"): return youtube.search(search_str=params.get("id", ""), @@ -285,33 +288,32 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: if xbmc.getCondVisibility("System.HasActiveModalDialog"): container_id = "" else: - container_id = "Container(%s)" % utils.get_infolabel( - "System.CurrentControlId") - dbid = utils.get_infolabel("%sListItem.DBID" % container_id) - db_type = utils.get_infolabel("%sListItem.DBType" % container_id) + container_id = f'Container({utils.get_infolabel("System.CurrentControlId")})' + dbid = utils.get_infolabel(f'{container_id}ListItem.DBID') + db_type = utils.get_infolabel(f'{container_id}ListItem.DBType') if db_type == "movie": params = {"dbid": dbid, - "id": utils.get_infolabel("%sListItem.Property(id)" % container_id), - "name": utils.get_infolabel("%sListItem.Title" % container_id)} + "id": utils.get_infolabel(f'{container_id}ListItem.Property(id)'), + "name": utils.get_infolabel(f'{container_id}ListItem.Title')} + utils.log(f'process.start_info_actions call exendedinfo with {params}') start_info_actions("extendedinfo", params) elif db_type == "tvshow": params = {"dbid": dbid, - "tvdb_id": utils.get_infolabel("%sListItem.Property(tvdb_id)" % container_id), - "id": utils.get_infolabel("%sListItem.Property(id)" % container_id), - "name": utils.get_infolabel("%sListItem.Title" % container_id)} + "tvdb_id": utils.get_infolabel(f'{container_id}ListItem.Property(tvdb_id)'), + "id": utils.get_infolabel(f'{container_id}ListItem.Property(id)'), + "name": utils.get_infolabel(f'{container_id}ListItem.Title')} start_info_actions("extendedtvinfo", params) elif db_type == "season": - params = {"tvshow": utils.get_infolabel("%sListItem.TVShowTitle" % container_id), - "season": utils.get_infolabel("%sListItem.Season" % container_id)} + params = {"tvshow": utils.get_infolabel(f'{container_id}ListItem.TVShowTitle'), + "season": utils.get_infolabel(f'{container_id}ListItem.Season')} start_info_actions("seasoninfo", params) elif db_type == "episode": - params = {"tvshow": utils.get_infolabel("%sListItem.TVShowTitle" % container_id), - "season": utils.get_infolabel("%sListItem.Season" % container_id), - "episode": utils.get_infolabel("%sListItem.Episode" % container_id)} + params = {"tvshow": utils.get_infolabel(f'{container_id}ListItem.TVShowTitle'), + "season": utils.get_infolabel(f'{container_id}ListItem.Season'), + "episode": utils.get_infolabel(f'{container_id}ListItem.Episode')} start_info_actions("extendedepisodeinfo", params) elif db_type in ["actor", "director"]: - params = {"name": utils.get_infolabel( - "%sListItem.Label" % container_id)} + params = {"name": utils.get_infolabel(f'{container_id}ListItem.Label')} start_info_actions("extendedactorinfo", params) else: utils.notify("Error", "Could not find valid content type") @@ -319,23 +321,22 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: if xbmc.getCondVisibility("System.HasModalDialog"): container_id = "" else: - container_id = "Container(%s)" % utils.get_infolabel( - "System.CurrentControlId") - dbid = utils.get_infolabel("%sListItem.DBID" % container_id) - db_type = utils.get_infolabel("%sListItem.DBType" % container_id) + container_id = f'Container({utils.get_infolabel("System.CurrentControlId")})' + dbid = utils.get_infolabel(f'{container_id}ListItem.DBID') + db_type = utils.get_infolabel(f'{container_id}ListItem.DBType') if db_type == "movie": params = {"dbid": dbid, - "id": utils.get_infolabel("%sListItem.Property(id)" % container_id), + "id": utils.get_infolabel(f'{container_id}ListItem.Property(id)'), "type": "movie"} start_info_actions("ratemedia", params) elif db_type == "tvshow": params = {"dbid": dbid, - "id": utils.get_infolabel("%sListItem.Property(id)" % container_id), + "id": utils.get_infolabel(f'{container_id}ListItem.Property(id)'), "type": "tv"} start_info_actions("ratemedia", params) if db_type == "episode": - params = {"tvshow": utils.get_infolabel("%sListItem.TVShowTitle" % container_id), - "season": utils.get_infolabel("%sListItem.Season" % container_id), + params = {"tvshow": utils.get_infolabel(f'{container_id}ListItem.TVShowTitle'), + "season": utils.get_infolabel(f'{container_id}ListItem.Season'), "type": "episode"} start_info_actions("ratemedia", params) elif info == 'youtubebrowser': @@ -344,62 +345,74 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - search_str = params.get("id", "") - if not search_str and params.get("search"): - result = xbmcgui.Dialog().input(heading=addon.LANG(16017), - type=xbmcgui.INPUT_ALPHANUM) - if result and result > -1: - search_str = result - else: - addon.clear_global('infodialogs.active') - return None - wm.open_video_list(search_str=search_str, - mode="search") - addon.clear_global('infodialogs.active') - elif info == 'extendedinfo': + try: + search_str = params.get("id", "") + if not search_str and params.get("search"): + result = xbmcgui.Dialog().input(heading=addon.LANG(16017), + type=xbmcgui.INPUT_ALPHANUM) + if result and result > -1: + search_str = result + else: + addon.clear_global('infodialogs.active') + return None + wm.open_video_list(search_str=search_str, + mode="search") + finally: + addon.clear_global('infodialogs.active') + elif info == 'extendedinfo': # called with movie id if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - wm.open_movie_info(movie_id=params.get("id"), - dbid=params.get("dbid"), - imdb_id=params.get("imdb_id"), - name=params.get("name")) - addon.clear_global('infodialogs.active') + try: + wm.open_movie_info(movie_id=params.get("id"), + dbid=params.get("dbid"), + imdb_id=params.get("imdb_id"), + name=params.get("name")) + finally: + addon.clear_global('infodialogs.active') elif info == 'extendedactorinfo': if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - wm.open_actor_info(actor_id=params.get("id"), - name=params.get("name")) - addon.clear_global('infodialogs.active') + try: + wm.open_actor_info(actor_id=params.get("id"), + name=params.get("name")) + finally: + addon.clear_global('infodialogs.active') elif info == 'extendedtvinfo': if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - wm.open_tvshow_info(tmdb_id=params.get("id"), - tvdb_id=params.get("tvdb_id"), - dbid=params.get("dbid"), - imdb_id=params.get("imdb_id"), - name=params.get("name")) - addon.clear_global('infodialogs.active') + try: + wm.open_tvshow_info(tmdb_id=params.get("id"), + tvdb_id=params.get("tvdb_id"), + dbid=params.get("dbid"), + imdb_id=params.get("imdb_id"), + name=params.get("name")) + finally: + addon.clear_global('infodialogs.active') elif info == 'seasoninfo': if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - wm.open_season_info(tvshow=params.get("tvshow"), - dbid=params.get("dbid"), - season=params.get("season")) - addon.clear_global('infodialogs.active') + try: + wm.open_season_info(tvshow=params.get("tvshow"), + dbid=params.get("dbid"), + season=params.get("season")) + finally: + addon.clear_global('infodialogs.active') elif info == 'extendedepisodeinfo': if addon.get_global('infodialogs.active'): return None addon.set_global('infodialogs.active', "true") - wm.open_episode_info(tvshow=params.get("tvshow"), - tvshow_id=params.get("tvshow_id"), - dbid=params.get("dbid"), - episode=params.get("episode"), - season=int(params.get("season"))) - addon.clear_global('infodialogs.active') + try: + wm.open_episode_info(tvshow=params.get("tvshow"), + tvshow_id=params.get("tvshow_id"), + dbid=params.get("dbid"), + episode=params.get("episode"), + season=int(params.get("season"))) + finally: + addon.clear_global('infodialogs.active') elif info == 'albuminfo': if params.get("id"): album_details = AudioDB.get_album_details(params.get("id")) @@ -464,8 +477,8 @@ def start_info_actions(info: str, params: Dict[str, str]) -> list: try: if os.path.isdir(path): shutil.rmtree(path) - except Exception as e: - utils.log(e) + except Exception as err: + utils.log(f'Failed to remove cache due to {err}') utils.notify("Cache deleted") elif info == 'tmdbpassword': addon.set_password_prompt("tmdb_password") diff --git a/script.extendedinfo/resources/lib/theaudiodb.py b/script.extendedinfo/resources/lib/theaudiodb.py new file mode 100644 index 000000000..9f24a4d99 --- /dev/null +++ b/script.extendedinfo/resources/lib/theaudiodb.py @@ -0,0 +1,311 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details +"""Module with get_* functions to query TADB + +Requires user API key (subscription basis) to access + +""" + +from __future__ import annotations + +import urllib.error +import urllib.parse +import urllib.request + +import xbmc +from resources.kutil131 import ItemList, addon + +from resources.kutil131 import AudioItem, VideoItem, local_db, utils + +AUDIO_DB_KEY = '2' #key no longer accepted - this is demo key +BASE_URL = 'https://www.theaudiodb.com/api/v1/json' +PLUGIN_BASE = 'plugin://script.extendedinfo/?info=' + + +def _handle_albums(results:dict) -> ItemList: + """Creates an ItemList of kutils131 AudioItems + + Args: + results (dict): TADB album info + + Returns: + ItemList: kutils131 ItemList of AudioItems + """ + albums = ItemList(content_type="albums") + if not results.get('album'): + return albums + local_desc = 'strDescription' + xbmc.getLanguage(xbmc.ISO_639_1).upper() + for item in results['album']: + desc = "" + if local_desc in item and item[local_desc]: + desc = item.get(local_desc, "") + elif item.get('strDescriptionEN'): + desc = item['strDescriptionEN'] + elif item.get('strDescription'): + desc = item['strDescription'] + if item.get('strReview'): + desc += f"[CR][CR][B]{addon.LANG(185)}:[/B][CR][CR]{item['strReview']}" + album = AudioItem(label=item['strAlbum'], + path="") + album.set_infos({'artist': item['strArtist'], + 'album': item['strAlbum'], + 'mediatype': "album", + 'genre': item['strGenre'], + 'year': item['intYearReleased']}) + album.set_properties({'mbid': item['strMusicBrainzID'], + 'id': item['idAlbum'], + 'audiodb_id': item['idAlbum'], + 'album_description': desc, + 'album_mood': item['strMood'], + 'album_style': item['strStyle'], + 'speed': item['strSpeed'], + 'album_Theme': item['strTheme'], + 'type': item['strReleaseFormat'], + 'loved': item['intLoved'], + 'location': item['strLocation'], + 'itunes_id': item['strItunesID'], + 'amazon_id': item['strAmazonID'], + 'sales': item['intSales']}) + album.set_artwork({'thumb': item['strAlbumThumb'], + 'spine': item['strAlbumSpine'], + 'cdart': item['strAlbumCDart'], + 'thumbback': item['strAlbumThumbBack']}) + albums.append(album) + return local_db.compare_album_with_library(albums) + + +def _handle_tracks(results: dict) -> ItemList: + """Creates an ItemList of track AudioItems + + Args: + results (dict): TADB tracks + + Returns: + ItemList: The kutils131 itemlist of the tracts + """ + tracks = ItemList(content_type="songs") + if not results.get('track'): + return tracks + for item in results['track']: + youtube_id = utils.extract_youtube_id(item.get('strMusicVid', '')) + track = AudioItem(label=item['strTrack'], + path=f"{PLUGIN_BASE}youtubevideo&&id={youtube_id}") + track.set_infos({'title': item['strTrack'], + 'album': item['strAlbum'], + 'artist': item['strArtist'], + 'mediatype': "song"}) + track.set_properties({'mbid': item['strMusicBrainzID']}) + track.set_artwork( + {'thumb': f"http://i.ytimg.com/vi/{youtube_id}/0.jpg"}) + tracks.append(track) + return tracks + + +def _handle_musicvideos(results:dict) -> ItemList: + """Creates an ItemList of TADB VideoItems + + Args: + results (dict): TADB musicvideos + + Returns: + ItemList: the kutils131 ItemList of musicvideos + """ + mvids = ItemList(content_type="musicvideos") + if not results.get('mvids'): + return mvids + for item in results['mvids']: + youtube_id = utils.extract_youtube_id(item.get('strMusicVid', '')) + mvid = VideoItem(label=item['strTrack'], + path=f"{PLUGIN_BASE}youtubevideo&&id={youtube_id}") + mvid.set_infos({'title': item['strTrack'], + 'plot': item['strDescriptionEN'], + 'mediatype': "musicvideo"}) + mvid.set_properties({'id': item['idTrack']}) + mvid.set_artwork( + {'thumb': f"http://i.ytimg.com/vi/{youtube_id}/0.jpg"}) + mvids.append(mvid) + return mvids + + +def extended_artist_info(results: dict) -> dict: + """Gets artist info from TADB and returns artist dict + + Args: + results (dict): TADB artist info + + Returns: + dict: artist details using Kodi properties keywords + """ + if not results.get('artists'): + return {} + local_bio = 'strBiography' + addon.setting("LanguageID").upper() + artist = results['artists'][0] + description = "" + if local_bio in artist and artist[local_bio]: + description = artist.get(local_bio) + elif artist.get('strBiographyEN'): + description = artist.get('strBiographyEN') + elif artist.get('strBiography'): + description = artist.get('strBiography') + if 'strReview' in artist and artist['strReview']: + description += "[CR]" + artist.get('strReview') + artist = {'label': artist.get('strArtist'), + 'artist': artist.get('strArtist'), + 'mediatype': "artist", + 'Country': artist.get('strCountry'), + 'mbid': artist.get('strMusicBrainzID'), + 'thumb': artist.get('strArtistThumb'), + 'Banner': artist.get('strArtistBanner'), + 'clearlogo': artist.get('strArtistLogo'), + 'fanart': artist.get('strArtistFanart'), + 'fanart2': artist.get('strArtistFanart2'), + 'fanart3': artist.get('strArtistFanart3'), + 'Artist_Mood': artist.get('strMood'), + 'Artist_Born': artist.get('intBornYear'), + 'Artist_Formed': artist.get('intFormedYear'), + 'Artist_Died': artist.get('intDiedYear'), + 'Artist_Disbanded': artist.get('strDisbanded'), + 'Artist_Description': description, + 'Artist_Genre': artist.get('strGenre'), + 'Artist_Style': artist.get('strStyle'), + 'CountryCode': artist.get('strCountryCode'), + 'Website': artist.get('strWebsite'), + 'Twitter': artist.get('strTwitter'), + 'Facebook': artist.get('strFacebook'), + 'LastFMChart': artist.get('strLastFMChart'), + 'Gender': artist.get('strGender'), + 'audiodb_id': artist.get('idArtist'), + 'Members': artist.get('intMembers')} + return artist + + +def get_artist_discography(search_str) -> ItemList: + """returns artist's discography + + Args: + search_str (str): Artist name + + Returns: + [ItemList]: kutils131 ItemList instance of AudioItems + """ + if not search_str: + return ItemList(content_type="albums") + params: dict = {"s": search_str} + results: dict = get_data("searchalbum", params) + return _handle_albums(results) + + +def get_artist_details(search_str) -> ItemList | dict: + """gets artist details from TADB + + Args: + search_str [str]: artist name + + Returns: + Union[ItemList, dict]: the extended artist info + """ + if not search_str: + return ItemList(content_type="artists") + params = {"s": search_str} + results = get_data("search", params) + if results: + return extended_artist_info(results) + else: + utils.notify("No artist info from TheAudioDb") + + +def get_most_loved_tracks(search_str="", mbid="") -> ItemList | list: + """ highest rated TADB soings for artist + + Args: + search_str (str, optional): artist name. Defaults to "". + mbid (str, optional): musicbrainz artist id. Defaults to "". + + Returns: + Union[ItemList, list]: list of songs + """ + if mbid: + url = 'track-top10-mb' + params = {"s": mbid} + elif search_str: + url = 'track-top10' + params = {"s": search_str} + else: + return [] + results = get_data(url, params) + return _handle_tracks(results) + + +def get_album_details(audiodb_id="", mbid="") -> ItemList | list: + """Creates ItemList of TADB alubm detals + + Args: + audiodb_id (str, optional): TADB album id "". + mbid (str, optional): mbid album groupd id Defaults to "". + + Returns: + list: empty if no results + ItemList: kutils131 ItemList of album AudioItems + """ + if audiodb_id: + url = 'album' + params = {"m": audiodb_id} + elif mbid: + url = 'album-mb' + params = {"i": mbid} + else: + return [] + results = get_data(url, params) + return _handle_albums(results)[0] + + +def get_musicvideos(audiodb_id) -> ItemList: + """Creates ItemList of musicvideo Videoitems + + Args: + audiodb_id (str): TADB id + + Returns: + ItemList: kutils131 ItemList + """ + if not audiodb_id: + return ItemList(content_type="musicvideos") + params = {"i": audiodb_id} + results = get_data("mvid", params) + return _handle_musicvideos(results) + + +def get_track_details(audiodb_id: str) -> ItemList | list: + """gets TADB info for a track + + Args: + audiodb_id (str): The TADB id + + Returns: + Union[ItemList, list]: List of track details + """ + if not audiodb_id: + return ItemList(content_type="songs") + params = {"m": audiodb_id} + results = get_data("track", params) + return _handle_tracks(results) + + +def get_data(url: str, params: dict) -> dict: + """returns a dict from TADB api query + + Args: + url (str): the TADB GET query + params (dict): TADB query pamaeters + + Returns: + dict: TADB api response + """ + tadb_key: str = addon.setting('TADB API Key') + if tadb_key is None or tadb_key == '': + tadb_key = AUDIO_DB_KEY #limited function key + params: dict = {k: str(v) for k, v in params.items() if v} + url: str = f"{BASE_URL}/{tadb_key}/{url}.php?{urllib.parse.urlencode(params)}" + return utils.get_JSON_response(url=url, + folder="TheAudioDB") diff --git a/script.extendedinfo/resources/lib/themoviedb.py b/script.extendedinfo/resources/lib/themoviedb.py new file mode 100644 index 000000000..8cfdcdd70 --- /dev/null +++ b/script.extendedinfo/resources/lib/themoviedb.py @@ -0,0 +1,1726 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +"""functions and classes to retrieve info from TMDB api v3 +see: https://developers.themoviedb.org/3/getting-started + +Public variables: + Login(LoginProvider): a TMDB session instance that logs into TMDB on + creation + +Public functions: + set_rating: sets local videodb userrating or TMDB user rating + (called from button in dialogvideoinfo) + change_fav_status: sets TMDB user favorite (called from button in dialogvideoinfo) + create_list: creates a new user list on TMDB + remove_list_dialog: opens a Kodi select dialog to allow user to select + a TMDB list for removal + change_list_status: Adds or removes a video item from user's TMDB list + get_account_lists: gets the user's TMDB lists + get_certification_list: gets the TMDB certifications ("MPAA") + handle_movies/tvshows/episodes: creates a kutils131 ItemList instance + of kutils131 VideoItems instances with Kodi listitem properties for the + video media type to display as Kodi container content items + handle_lists: adds user TMDB lists to kutils131 ItemList instance for display + in Kodi cantainer content + handle_seasons: adds seasons to kutils131 ItemList instance + handle_videos: adds video clips as kutils131 VideoItems to kutils131 ItemList instance + search_companies: gets the TMDB company ID for company (studio) name string + multi_search: performs TMDB multisearch "Multi search currently supports + searching for movies, tv shows and people in a single request." + get_list_movies: query TMDB movie list for TMDB id + get_person_info: query TMDB actors/crew details for person name(s) + get_keywords: query TMDB keywords by keyword string for TMDB id + get_set_id: query TMDB sets by string for TMDB id + get_data: query TMDB with various search terms + get_company_data: query TMDB for company data using TMDB id + get_credit_info: query TMDB for credit data using TMDB id + get_account_props: provide various TMDB account related info as a dict + get_movie_tmdb_id: queries TMDB for TMDB video id if IMDB id is in + local videodb + get_show_tmdb_id: queries TMDB for TMDB tvshow id if TVDB id is in + local videodb + get_movie_videos: queries TMDB for movie trailers + extended_movie_info: sets Kodi listitem properties as a kutils + VideoItem instance and additionally returns a dict of kutil itemlists + instances + get_tvshow: queries TMDB for tvshow details as dict + extended_tvshow_info: sets Kodi listitem properties as a kutils + VideoItem instance and additionally returns a dict of kutil itemlists + instances + extended_season_info: sets Kodi listitem properties as a kutils + VideoItem instance and additionally returns a dict of kutil itemlists + instances + get_episode: queries TMDB for episode details as dict + extended_episode_info: sets Kodi listitem properties as a kutils + VideoItem instance and additionally returns a dict of kutil itemlists + instances + extended_actor_info: sets Kodi listitem properties as a kutils + VideoItem instance and additionally returns a dict of kutil itemlists + instances + get_movie_lists: gets kutils131 ItemList instance for movie lists + get_rated_media_items: queries TMDB for user media ratings + get_fav_items: queries TMDB for user favorites + get_movies_from_list: queries TMDB for movie list + get_popular_actors: queries TMDB for popular actors + get_movie: queries TMDB for movie given TMDB id + get_similar_movies: queries TMDB for similar movies + get_similar_tvshows: queries TMDB for similar tvshows + get_tvshows/movies: queries TMDB for tvshows/movies matching type + get_set_movies: queries TMDB for itemlist of movies in set + get_person_movies: queries TMDB for itemlist of movies for actor + search_media: generalized TMDB query + +""" + +from __future__ import annotations + +import re +import traceback +import urllib.error +import urllib.parse +import urllib.request + +from resources.kutil131 import (ItemList, VideoItem, addon, kodiaddon, kodijson, local_db, + selectdialog, utils) + +TMDB_TOKEN = ('wpjCrsKBwpjClnvCmsKiwoDCmcKCf8KFwrB8ZsKFwpl-bWXCnsKqesKgwprCh8K' + 'HwpzChMKgesKhwoJ7wqXCmn3CssKIwpzChcKHwpjCscKJwojCisKiwofCm37CoMK' + 'QdHdlwoJ7wonCrsKBacKKYX3Csn9iwoN3wpjCsX93fcKqwoLCn35pwo_CmX9pfsK' + 'hwonCrcKBe8KGwqh9wqV7ZcKQwop6wrF-ZsKJwq7ChnVxbcKEdMKLwqrCj8KkfsK' + 'fwo3CisKCwqR5wqbChMKawphmd8KjwpPCrX1twpDCqnrCoMKZd8KiwpnCmMKkwob' + 'CnMKOesKDwpV8e8KAYsKQwot_wrHCkcKLbcKswoLCm8KfwrDCnMKBZHfCgMKAwpT' + 'CjsKJwpzCr8KWwpbCkcKnwoV5wpliesKYaWzChcKJwovCoMKua3htwojCj396wox' + '8woLChcKQwqDCscKDwpHCpMKe') +POSTER_SIZES = ["w92", "w154", "w185", "w342", "w500", "w780", "original"] +LOGO_SIZES = ["w45", "w92", "w154", "w185", "w300", "w500", "original"] +BACKDROP_SIZES = ["w300", "w780", "w1280", "original"] +PROFILE_SIZES = ["w45", "w185", "h632", "original"] +STILL_SIZES = ["w92", "w185", "w300", "original"] +HEADERS = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'User-agent': 'Kodi/19.0 ( scott967@kodi.tv )', + 'Authorization': '' +} +IMAGE_BASE_URL = "http://image.tmdb.org/t/p/" +POSTER_SIZE = "w500" +URL_BASE = "https://api.themoviedb.org/3/" +ALL_MOVIE_PROPS = ("account_states,alternative_titles,credits,images,keywords," + "release_dates,videos,translations,similar,reviews,lists,rating") +ALL_TV_PROPS = ("account_states,alternative_titles,content_ratings,credits," + "external_ids,images,keywords,rating,similar,translations,videos") +ALL_ACTOR_PROPS = "tv_credits,movie_credits,combined_credits,images,tagged_images" +ALL_SEASON_PROPS = "videos,images,external_ids,credits" +ALL_EPISODE_PROPS = "account_states,credits,external_ids,images,rating,videos" + +PLUGIN_BASE = "plugin://script.extendedinfo/?info=" + +RELEASE_TYPES = {1: "Premiere", + 2: "Theatrical (limited)", + 3: "Theatrical", + 4: "Digital", + 5: "Physical", + 6: "TV"} + +GENDERS = {1: addon.LANG(32095), + 2: addon.LANG(32094)} + +STATUS = {"released": addon.LANG(32071), + "post production": addon.LANG(32072), + "in production": addon.LANG(32073), + "ended": addon.LANG(32074), + "returning series": addon.LANG(32075), + "planned": addon.LANG(32076)} + + +def _mulitple_repl(text:str) -> str: + """ replaces html tags in text with Kodi label formats + TRANS is a literal dict with regex patterns to match and replace + Args: + text (str): string to replace tags + + Returns: + str: string with Kodi formatting + + """ + TRANS = {"": "", + "": "[B]", + "": "[/B]", + "": "[I]", + "": "[/I]"} + regex = re.compile(f"({'|'.join(map(re.escape, TRANS.keys()))})") + return regex.sub(lambda mo: TRANS[mo.group()], text) + +class LoginProvider: + """ + logs into TMDB for user or guest and gets corresponding session or guest session id + """ + + def __init__(self, *args, **kwargs) -> LoginProvider: + """Creates a new user for accessing tmdb + + Returns: + LoginProvider: session for user + """ + self.session_id = None + self.logged_in = False + self.account_id = None + self.username = kwargs.get("username") + self.password = kwargs.get("password") + + def reset_session_id(self): + """Resets the user session_id in settings when tmdb authentication fails + This will require obraining a new session_id via get_session_id + """ + utils.log('tmdb.LoginProvider tmdb authentication failed, resetting session_id') + if addon.setting("session_id"): + addon.set_setting("session_id", "") + + + def check_login(self) -> bool: + """determines if user has an active login (session id) on tmdb when opening a tmdb-based + dialog + see https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id + for the tmdb protocol + Note: in api v4 this will become mandatory. Optional in v3 + Note2: checks addon settings for a saved session id first. + + Returns: + bool: true if user has an active session id from tmdb + """ + if self.username and self.password: + return bool(self.get_session_id()) + return False + + def get_account_id(self) -> str: + """returns TMDB account id. Requires an active session id + + Returns: + str: the tmdb account id or None + """ + if self.account_id: + return self.account_id + self.session_id = self.get_session_id() + response = get_data(url="account", + params={"session_id": self.session_id}, + cache_days=999999) + if not response: + return None + self.account_id = response.get("id") + return self.account_id + + def get_guest_session_id(self) -> str: + """returns guest session id for TMDB guest session has limited privledge + + Returns: + str: tmdb guest session id or None + """ + response = get_data(url="authentication/guest_session/new", + cache_days=9999) + if not response or "guest_session_id" not in response: + return None + return str(response["guest_session_id"]) + + def test_session_id(self, session_id) -> bool: + """tests session_id by getting account_id + If no account_id returned session_id is invalid. + + Args: + session_id (str): a session_id stored in settings + + Returns: + bool: True if session_id got and account_id + """ + response = get_data(url="account", + params={"session_id": session_id}, + cache_days=999999) + if response and "status_code" in response: + return response.get("status_code") == 1 + return False + + def get_session_id(self, cache_days=999) -> str: + """gets the tmdb session id from addon settings or creates one if not found + + Args: + cache_days (int, optional): Days to cache a session id in settings. + Defaults to 999. + + Returns: + str: the tmdb session id + """ + if addon.setting("session_id"): + self.session_id = addon.setting("session_id") + if self.test_session_id(self.session_id): + return addon.setting("session_id") + self.create_session_id() + return self.session_id + + def create_session_id(self) -> None: + """gets session id from tmdb as self.session_id and saves it in addon settings + 1. get request token from tmdb + 2. create session with login using username/pass + """ + response = get_data(url="authentication/token/new", + cache_days=0) + params = {"request_token": response["request_token"], + "username": self.username, + "password": self.password} + response = get_data(url="authentication/token/validate_with_login", + params=params, + cache_days=0) + if response and response.get("success"): + request_token = response["request_token"] + response = get_data(url="authentication/session/new", + params={"request_token": request_token}) + if response and "success" in response: + self.session_id = str(response["session_id"]) + addon.set_setting("session_id", self.session_id) + + +def set_rating(media_type, media_id, rating, dbid=None): + '''Sets rating for a user media item on tmdb account and Kodi userrating + media_type: movie, tv or episode + media_id: tmdb_id / episode ident array + rating: rating value (1 - 10, 0 for deleting) + dbid: dbid for syncing userrating of db item + ''' + if not media_type or not media_id or rating == -1: + return False + if dbid: + kodijson.set_userrating(media_type, dbid, rating) + params = {} + if Login.check_login(): + params["session_id"] = Login.get_session_id() + else: + params["guest_session_id"] = Login.get_guest_session_id() + utils.log('tmdb.set_rating no login use guest session id') + if media_type == "episode": + if not media_id[1]: + media_id[1] = "0" + url = f"tv/{media_id[0]}/season/{media_id[1]}/episode/{media_id[2]}/rating" + else: + url = f"{media_type}/{media_id}/rating" + results = send_request(url=url, + params=params, + values={"value": "%.1f" % + float(rating)} if rating > 0 else {}, + delete=rating == 0) + if results: + utils.notify(addon.NAME, results["status_message"]) + return True + + +def send_request(url, params, values, delete=False): + """formats a tmdb api query url + + Args: + url (_type_): _description_ + params (_type_): _description_ + values (_type_): _description_ + delete (bool, optional): request is a post or delete. Defaults to False. + + Returns: + _type_: _description_ + """ + HEADERS['Authorization'] = 'Bearer ' + kodiaddon.decode_string(TMDB_TOKEN, uuick=addon.setting('tmdb_tok')) + params = {k: str(v) for k, v in params.items() if v} + url = f"{URL_BASE}{url}?{urllib.parse.urlencode(params)}" + if delete: + return utils.delete(url, values=values, headers=HEADERS) + else: + return utils.post(url, values=values, headers=HEADERS) + + +def change_fav_status(media_id=None, media_type="movie", status="true") -> str: + """Updates user favorites list on tmdb + + Args: + media_id (_type_, optional): tmdb id. Defaults to None. + media_type (str, optional): _tmdb medi type. Defaults to "movie". + status (str, optional): tmdb favorite status. Defaults to "true". + + Returns: + str: tmdb result status message + """ + session_id = Login.get_session_id() + if not session_id: + utils.notify("Could not get session id") + return None + values = {"media_type": media_type, + "media_id": media_id, + "favorite": status} + results = send_request(url=f"account/{Login.get_account_id()}/favorite", + params={"session_id": session_id}, + values=values) + if results: + utils.notify(addon.NAME, results["status_message"]) + + +def create_list(list_name): + ''' + creates new list on TMDB with name *list_name + returns newly created list id + ''' + values = {'name': list_name, + 'description': 'List created by ExtendedInfo Script for Kodi.'} + results = send_request(url="list", + params={"session_id": Login.get_session_id()}, + values=values) + if results: + utils.notify(addon.NAME, results["status_message"]) + return results["list_id"] + + +def remove_list_dialog(account_lists) -> bool: + """opens select dialog to remove list from user tmdb lists + + Args: + account_lists (_type_): user's existing tgmdb lists + + Returns: + bool: True if user successfully removed a list + """ + index = selectdialog.open(header=addon.LANG(32138), + listitems=account_lists) + if index >= 0: + remove_list(account_lists[index]["id"]) + return True + + +def remove_list(list_id): + """removes user selected list from user's tmdb lists + raises toast with results from tmdb + + Args: + list_id (_type_): id of user tmdb list to remove + + Returns: + _type_: _description_ + """ + results = send_request(url=f"list/{list_id}", + params={"session_id": Login.get_session_id()}, + values={'media_id': list_id}, + delete=True) + if results: + utils.notify(addon.NAME, results["status_message"]) + return results["list_id"] + + +def change_list_status(list_id, movie_id, status): + """adds or removes item from user tmdb list + raises toast with results from tmdb + + Args: + list_id (_type_): the user list to update + movie_id (_type_): th eitem tmdb id to update + status (_type_): add or remove item + """ + method = "add_item" if status else "remove_item" + results = send_request(url=f"list/{list_id}/{method}", + params={"session_id": Login.get_session_id()}, + values={'media_id': movie_id}) + if results: + utils.notify(addon.NAME, results["status_message"]) + + +def get_account_lists(cache_days=0): + ''' + returns movie lists for TMDB user + ''' + session_id = Login.get_session_id() + account_id = Login.get_account_id() + if not session_id or not account_id: + return [] + response = get_data(url=f"account/{account_id}/lists", + params={"session_id": session_id}, + cache_days=cache_days) + return response["results"] + + +def get_certification_list(media_type:str) -> dict[str,list]: + """Gets the known certifications and meanings from tmdb + The list is cached since it doesn't change (much?) + + Args: + media_type (str): movie/tv + + Returns: + dict[str,list]: a dict keys are ISO-1366-1 countries + values are list of certs and their descriptions + """ + response = get_data(url=f"certification/{media_type}/list", + cache_days=999999) + return response.get("certifications") + + +def merge_with_cert_desc(input_list: ItemList, media_type: str) -> ItemList: + """adds the tmdb text description of certification + + Args: + input_list (ItemList): the ItemList of releases (with country and cert) + media_type (str): movie or tv + + Returns: + ItemList: the input ItemList with cert meanings appended to the VideoItems + """ + cert_list = get_certification_list(media_type) + for item in input_list: + iso = item.get_property("iso_3166_1") + if iso not in cert_list: + continue + hit = utils.dictfind(lst=cert_list[iso], + key="certification", + value=item.get_property("certification")) + if hit: + item.set_property("meaning", hit["meaning"]) + return input_list + +def get_best_release(co_releases:list) -> dict: + """Gets a single release for a country with multiple releases + Ensures the release has a cert. Best release is release with + "lowest" tmdb release type value + + Args: + co_releases (list): _description_ + + Returns: + dict: _description_ + """ + best_release = {} + for release in co_releases: + if not release.get('certification'): + continue + if not best_release: + best_release = release + elif int(release.get('type')) < int(best_release.get('type')): + best_release = release + return best_release + + +def handle_multi_search(results): + listitems = ItemList(content_type="videos") + for item in results: + if item["media_type"] == "movie": + listitems.append(handle_movies([item])[0]) + elif item["media_type"] == "tv": + listitems.append(handle_tvshows([item])[0]) + elif item["media_type"] == "person": + listitems.append(handle_people([item])[0]) + return listitems + + +def handle_movies(results: list[dict], local_first=True, sortkey="year") ->ItemList[VideoItem]: + """takes a list of movies (dicts) and adds local db data and then sorts as an ItemList + The tmdb movie keys are converted to extendedinfo keys and genre ids converted + to localized text strings, then a VideoItem is created for each movie. The + Kodi videodb is searched for any matching local movies and these are added to + the ItemList as VideoItems. + + Args: + results (List[dict]): a list of movies, each movie is a dict + local_first (bool, optional): should movies in the db list first. Defaults to True. + sortkey (str, optional): key to sort the movies. Defaults to "year". + + Returns: + ItemList: a kutils131 ItemList of the movies to display in a Kodi container + """ + response: dict = get_data(url="genre/movie/list", + params={"language": addon.setting("LanguageID")}, + cache_days=30) + ids: list[int] = [item["id"] for item in response["genres"]] + labels: list[str] = [item["name"] for item in response["genres"]] + movies = ItemList(content_type="movies") + path = 'extendedinfo&&id=%s' if addon.bool_setting( + "infodialog_onclick") else "playtrailer&&id=%s" + for movie in results: + genres = [labels[ids.index(id_)] for id_ in movie.get( + "genre_ids", []) if id_ in ids] + item = VideoItem(label=movie.get('title'), + path=PLUGIN_BASE + path % movie.get("id")) + release_date = movie.get('release_date') + item.set_infos({'title': movie.get('title'), + 'originaltitle': movie.get('original_title', ""), + 'mediatype': "movie", + 'country': movie.get('original_language'), + 'plot': movie.get('overview'), + 'Trailer': f"{PLUGIN_BASE}playtrailer&&id={movie.get('id')}", + 'genre': " / ".join([i for i in genres if i]), + 'votes': movie.get('vote_count'), + 'year': utils.get_year(release_date), + 'rating': round(movie['vote_average'], 1) if movie.get('vote_average') else "", + 'userrating': movie.get('rating'), + 'premiered': release_date}) + item.set_properties({'id': movie.get("id"), + 'popularity': round(movie['popularity'], 1) if movie.get('popularity') else "", + 'credit_id': movie.get('credit_id'), + 'character': movie.get('character'), + 'job': movie.get('job'), + 'department': movie.get('department')}) + item.set_artwork(get_image_urls(poster=movie.get("poster_path"), + fanart=movie.get("backdrop_path"))) + movies.append(item) + return local_db.merge_with_local(media_type="movie", + items=movies, + library_first=local_first, + sortkey=sortkey) + + +def handle_tvshows(results:list[dict], local_first=True, sortkey="year"): + tvshows = ItemList(content_type="tvshows") + response = get_data(url="genre/tv/list", + params={"language": addon.setting("LanguageID")}, + cache_days=30) + ids = [item["id"] for item in response["genres"]] + labels = [item["name"] for item in response["genres"]] + for tv in results: + tmdb_id = tv.get("id") + genres = [labels[ids.index(id_)] + for id_ in tv.get("genre_ids", []) if id_ in ids] + duration = "" + if "episode_run_time" in tv: + if len(tv["episode_run_time"]) > 1: + duration = "%i - %i" % (min(tv["episode_run_time"]), + max(tv["episode_run_time"])) + elif len(tv["episode_run_time"]) == 1: + duration = "%i" % (tv["episode_run_time"][0]) + newtv = VideoItem(label=tv.get('name'), + path=f'{PLUGIN_BASE}extendedtvinfo&&id={tmdb_id}') + newtv.set_infos({'originaltitle': tv.get('original_name', ""), + 'title': tv.get('name'), + 'duration': duration, + 'genre': " / ".join([i for i in genres if i]), + 'country': tv.get('original_language'), + 'plot': tv.get("overview"), + 'year': utils.get_year(tv.get('first_air_date')), + 'mediatype': "tvshow", + 'rating': round(tv['vote_average'], 1) if tv.get("vote_average") else "", + 'userrating': tv.get('rating'), + 'votes': tv.get('vote_count'), + 'premiered': tv.get('first_air_date')}) + newtv.set_properties({'id': tmdb_id, + 'character': tv.get('character'), + 'popularity': round(tv['popularity'], 1) if tv.get('popularity') else "", + 'credit_id': tv.get('credit_id'), + 'totalepisodes': tv.get('number_of_episodes'), + 'totalseasons': tv.get('number_of_seasons')}) + newtv.set_artwork(get_image_urls(poster=tv.get("poster_path"), + fanart=tv.get("backdrop_path"))) + tvshows.append(newtv) + tvshows = local_db.merge_with_local(media_type="tvshow", + items=tvshows, + library_first=local_first, + sortkey=sortkey) + return tvshows + + +def handle_episodes(results:list[dict]) -> ItemList[VideoItem]: + """Creates an ItemList of VideoItems for episodes + + Args: + results (_type_): tmdb episode details + + Returns: + _type_: Kutils131 ItemList of episode VideoItmes + """ + listitems = ItemList(content_type="episodes") + for item in results: + title = item.get("name") + if not title: + title = "%s %s" % (addon.LANG(20359), item.get('episode_number')) + listitem = {'label': title} + listitem = VideoItem(label=title, + artwork=get_image_urls(still=item.get("still_path"))) + listitem.set_infos({'mediatype': "episode", + 'title': title, + 'aired': item.get('air_date'), + 'episode': item.get('episode_number'), + 'season': item.get('season_number'), + 'code': item.get("production_code"), + 'userrating': item.get('rating'), + 'plot': item.get('overview'), + 'rating': round(item['vote_average'], 1) if item.get('vote_average') else "", + 'votes': item.get('vote_count')}) + listitem.set_properties({'id': item.get('id'), + 'production_code': item.get('production_code')}) + listitems.append(listitem) + return listitems + + +def handle_release_dates(results:list[dict]) -> ItemList[VideoItem]: + """Creates ItemList of video mpaa cert and dates as VideoItems + + Args: + results (list[dict]): a list of dicts with TMDB releases + + Returns: + ItemList: ItemList of releases/certs + """ + listitems = ItemList() + for item in results: + if len(item["release_dates"]) == 1: + ref:dict = item["release_dates"][0] #only handles the first listed release per country + if not ref.get("certification"): + continue + #listitem = VideoItem(label=item.get('name')) #no key 'name' + listitem = VideoItem(label='') + listitem.set_properties({'certification': ref.get('certification'), + 'iso_3166_1': item.get('iso_3166_1', ""), + 'note': ref.get('note'), + 'iso_639_1': ref.get('iso_639_1'), + 'release_date': ref.get('release_date'), + 'type': RELEASE_TYPES.get(ref.get('type'))}) + else: + ref:dict = get_best_release(item["release_dates"]) + if not ref: + continue + listitem = VideoItem(label='') + listitem.set_properties({'certification': ref.get('certification'), + 'iso_3166_1': item.get('iso_3166_1', ""), + 'note': ref.get('note'), + 'iso_639_1': ref.get('iso_639_1'), + 'release_date': ref.get('release_date'), + 'type': RELEASE_TYPES.get(ref.get('type'))}) + listitems.append(listitem) + return listitems + + +def handle_content_ratings(results:list[dict]) -> ItemList[VideoItem]: + listitems = ItemList() + for item in results: + listitem = VideoItem(label=item['rating']) + listitem.set_properties({'iso_3166_1': item['iso_3166_1'].lower(), + 'certification': item['rating'].lower()}) + listitems.append(listitem) + return listitems + + +def handle_reviews(results:list[dict]) -> ItemList[VideoItem]: + """Creates an ItemList of VideoItems for tmdb reviews + + Args: + results (_type_): tmdb review details + + Returns: + ItemList: Kutils131 ItemList of review VideoItmes + """ + listitems = ItemList() + for item in results: + listitem = VideoItem(label=item.get('author')) + listitem.set_properties({'author': item.get('author'), + 'content': _mulitple_repl(item.get('content')).lstrip(), + 'id': item.get('id'), + 'url': item.get('url')}) + listitems.append(listitem) + return listitems + + +def handle_text(results:list[dict]) -> ItemList[VideoItem]: + """Converts list of textual info (genres, countries) to ItemList + + Args: + results (list[dict]): a list of related text info + + Returns: + ItemList: an ItemList of the text info as VideoItems + """ + listitems = ItemList() + for item in results: + listitem = VideoItem(label=item.get('name')) + listitem.set_property("id", item.get('id')) + listitems.append(listitem) + return listitems + + +def handle_lists(results:list[dict]) -> ItemList[VideoItem]: + """Converts various tmdb user movie lists to ItemList + + Args: + results (list[dict]): list of user movie collections each item is a collection + + Returns: + ItemList: ItemList of collections as VideoItems type of 'set' + """ + listitems = ItemList(content_type="sets") + for item in results: + listitem = VideoItem(label=item.get('name'), + path="plugin://script.extendedinfo?info=listmovies&---id=%s" % item.get( + 'id'), + artwork=get_image_urls(poster=item.get("poster_path"))) + listitem.set_infos({'plot': item.get('description'), + "media_type": "set"}) + listitem.set_properties({'certification': item.get('certification', "") + item.get('rating', ""), + 'item_count': item.get('item_count'), + 'favorite_count': item.get('favorite_count'), + 'iso_3166_1': item.get('iso_3166_1', "").lower(), + 'id': item.get('id')}) + listitems.append(listitem) + return listitems + + +def handle_seasons(results:list[dict]) -> ItemList[VideoItem]: + """Creates an ItemList of VideoItems for seasons + + Args: + results (_type_): tmdb season details + + Returns: + ItemList: Kutils131 ItemList of season VideoItmes + """ + listitems = ItemList(content_type="seasons") + for item in results: + season = item.get('season_number') + listitem = VideoItem(label=addon.LANG(20381) if season == 0 else "%s %s" % (addon.LANG(20373), season), + properties={'id': item.get('id')}, + artwork=get_image_urls(poster=item.get("poster_path"))) + listitem.set_infos({'mediatype': "season", + 'season': season, + 'premiered': item.get('air_date'), + 'year': utils.get_year(item.get('air_date'))}) + listitems.append(listitem) + return listitems + + +def handle_videos(results:list[dict]) -> ItemList[VideoItem]: + """Creates an ItemList of video clips/trailers as VideoItems + + Args: + results (list[dict]): list of clips/trailers + + Returns: + ItemList: ItemList of VideoItems of the clips/trailers + """ + listitems = ItemList(content_type="videos") + for item in results: + listitem = VideoItem(label=item.get('name'), + size=item.get('size'), + artwork={'thumb': "http://i.ytimg.com/vi/%s/0.jpg" % item.get('key')}) + listitem.set_infos({'mediatype': "video"}) + listitem.set_properties({'iso_639_1': item.get('iso_639_1'), + 'type': item.get('type'), + 'key': item.get('key'), + 'youtube_id': item.get('key'), + 'site': item.get('site'), + 'id': item.get('id')}) + listitems.append(listitem) + return listitems + + +def handle_people(results:list[dict], select: bool = False) -> ItemList[VideoItem]: + """converts list of tmdb people into kutils131 videoitems + The VideoItem properties are tmdb query results + + Args: + results (list[dict]): a list of dicts, each dict is tmdb data for a person + select (bool): True if people are to be added to select dialog listing + + Returns: + ItemList: A kutils131 ItemList of VideoItems for tmdb persons + """ + people = ItemList(content_type="actors") + for item in results: + name_ext: str = '' + if item.get('adult'): + name_ext = name_ext + ' Adult' + if item.get('known_for_department'): + name_ext = name_ext + f' {item["known_for_department"]}' + if name_ext != '': + name_ext = f' -{name_ext}' + person = VideoItem(label=item['name'] if not select else f'{item["name"]}{name_ext}', + path=f"{PLUGIN_BASE}extendedactorinfo&&id={item['id']}", + infos={'mediatype': "artist"}, + artwork=get_image_urls(profile=item.get("profile_path"))) + person.set_properties({'adult': item.get('adult'), + 'alsoknownas': " / ".join(item.get('also_known_as', [])), + 'biography': item.get('biography'), + 'birthday': item.get('birthday'), + 'age': utils.calculate_age(item.get('birthday'), item.get('deathday')), + 'character': item.get('character'), + 'department': item.get('department'), + 'job': item.get('job'), + 'id': item['id'], + 'cast_id': item.get('cast_id'), + 'credit_id': item.get('credit_id'), + 'deathday': item.get('deathday'), + 'placeofbirth': item.get('place_of_birth'), + 'homepage': item.get('homepage'), + 'known_for_department': item.get('known_for_department')}) + people.append(person) + return people + + +def handle_images(results:list[dict]) -> ItemList[VideoItem]: + """creates VideoItems of images and returns an ItemList + + Args: + results (list[dict]): image list + + Returns: + ItemList: kutils131 itemlist of the images as VideoItems type 'music'? + """ + images = ItemList(content_type="images") + for item in results: + artwork = get_image_urls(poster=item.get("file_path")) #artwork is a dict key imagetype value path to image + image = VideoItem(artwork=artwork) + image.set_properties({'aspectratio': item['aspect_ratio'], + 'type': "poster" if item['aspect_ratio'] < 0.7 else "fanart", + 'rating': item.get("vote_average"), + 'votes': item.get("vote_count"), + 'iso_639_1': item.get("iso_639_1")}) + if item.get("media") and item["media"].get("title"): #tmdb has 2 media returns: movie and tv + image.set_label(item["media"].get("title")) + image.set_property("movie_id", item["media"].get("id")) + poster_path = item["media"].get("poster_path") + if poster_path: + image.update_artwork( + {'mediaposter': IMAGE_BASE_URL + POSTER_SIZE + poster_path}) + image.set_info("mediatype", "music") + images.append(image) + return images + + +def handle_companies(results:list[dict]) -> ItemList[VideoItem]: + """Converts list of studios from tmdb to ItemList of VideoItems + + Args: + results (list[dict]): tmdb "production comapnies" list + + Returns: + ItemList: ItemList of studio VideoItems + """ + companies = ItemList(content_type="studios") + for item in results: + company = VideoItem(label=item['name'], + infos={'plot': item.get('description')}) + company.set_properties({'parent_company': item.get('parent_company'), + 'headquarters': item.get('headquarters'), + 'homepage': item.get('homepage'), + 'id': item['id']}) + art = "resource://resource.images.studios.white/{}.png".format( + item['name']) if item['name'] else "" + company.set_artwork({"thumb": art, + "icon": art}) + companies.append(company) + return companies + + +def search_companies(company_name): + regex = re.compile(r'\(.+?\)') + response = get_data(url="search/company", + params={"query": regex.sub('', company_name)}, + cache_days=10) + if response and "results" in response: + return handle_companies(response["results"]) + else: + return None + + +def multi_search(search_str, page=1, cache_days=1): + params = {"query": search_str, + "include_adult": addon.setting("include_adults").lower(), + "page": page} + response = get_data(url="search/multi", + params=params, + cache_days=cache_days) + if response and "results" in response: + itemlist = handle_multi_search(response["results"]) + itemlist.set_totals(response["total_results"]) + return itemlist + + +def get_list_movies(list_id, force): + url = "list/%s" % (list_id) + response = get_data(url=url, + params={"language": addon.setting("LanguageID")}, + cache_days=0 if force else 2) + if not response: + return None + items = handle_movies(results=response["items"], + local_first=True, + sortkey=None) + itemlist = ItemList(items=items) + itemlist.set_totals(len(response["items"])) + return itemlist + + +def get_person_info(person_label:str, skip_dialog=False) -> dict: + """takes an actor name and returns actor info for it + + Args: + person_label (str): the actor name, or names with ' / ' as separator + skip_dialog (bool, optional): Option to show user select dialog for + mulitple tmdb names. Defaults to False. + If true first actor in response is returned + + Returns: + dict: a dict of info for the named actor -- keys + 'adult' : bool + 'gender' : int enum + 'id' : int + 'known_for' : list of dicts of media info + 'known_for_department' : str enum + 'name' : str + 'popularity' : float + 'profile_path' : str of image path/filename + False: if no response + + """ + if not person_label: + return False + params = {"query": person_label.split(" / ")[0], + "include_adult": addon.setting("include_adults").lower()} + response = get_data(url="search/person", + params=params, + cache_days=30) + if not response or "results" not in response: + return False + people: list[dict] = [i for i in response["results"] if i["name"] == person_label] + if len(people) > 1 and not skip_dialog: + index = selectdialog.open(header=f'{addon.LANG(32151)} TMDB People', + listitems=handle_people(people, select=True)) + return people[index] if index > -1 else False + elif people: + return people[0] + elif response["results"]: + return response["results"][0] + return False + + +def get_keywords(search_label): + params = {"query": search_label, + "include_adult": addon.setting("include_adults").lower()} + response = get_data(url="search/keyword", + params=params, + cache_days=30) + if not response or not response.get("results"): + return False + return response["results"] + + +def get_set_id(set_name): + params = {"query": set_name.replace("[", "").replace("]", "").replace("Kollektion", "Collection"), + "language": addon.setting("LanguageID")} + response = get_data(url="search/collection", + params=params, + cache_days=14) + if not response or not response.get("results"): + return "" + return response["results"][0]["id"] + + +def get_data(url:str = "", params:dict = None, cache_days:float = 14) -> dict|None: + """Queries tmdb api v3 or local cache + + Args: + url (str, optional): tmdb query terms for the search apiv3. Defaults to "". + params (dict, None): Dict of optional parameters for + query. Defaults to None. + cache_days (float, optional): Days to check for cached values. + Defaults to 14. + + Returns: + response(dict): A dict of JSON.loads response from TMDB or None if no + TMDB response + """ + params = params if params else {} + HEADERS['Authorization'] = 'Bearer ' + kodiaddon.decode_string(TMDB_TOKEN, uuick=addon.setting('tmdb_tok')) + params = {k: str(v) for k, v in params.items() if v} + url = "%s%s?%s" % (URL_BASE, url, urllib.parse.urlencode(params)) + response = utils.get_JSON_response(url, cache_days, folder='TheMovieDB', headers=HEADERS) + if not response: + utils.log("tmdb.get_data No response from TMDB") + return None + if "status_code" in response and response.get("status_code") != 1: + utils.log(f'tmdb.get_data FAIL TMDB status code: {response.get("status_code")} - {response.get("status_message")} {traceback.format_stack(limit=-3)}') + if response.get('status_code') == 3: + Login.reset_session_id() + return None + return response + + +def get_company_data(company_id): + if not company_id: + return [] + response = get_data(url="company/%s/movies" % (company_id), + cache_days=30) + if not response or not response.get("results"): + return [] + return handle_movies(response["results"]) + + +def get_credit_info(credit_id): + if not credit_id: + return [] + return get_data(url=f"credit/{credit_id}", + params={"language": addon.setting("LanguageID")}, + cache_days=30) + + +def get_account_props(states) -> dict: + return {"FavButton_Label": addon.LANG(32155) if states.get("favorite") else addon.LANG(32154), + "favorite": "True" if states.get("favorite") else "", + "rated": int(states["rated"]["value"]) if states["rated"] else "", + "watchlist": states["watchlist"] if "watchlist" in states else ""} + + +def get_image_urls(poster=None, still=None, fanart=None, profile: str =None) -> dict: + ''' + get a dict with all available images for given image types + ''' + images = {} + if poster: + images["poster"] = IMAGE_BASE_URL + "w500" + poster + images["poster_original"] = IMAGE_BASE_URL + "original" + poster + images["original"] = IMAGE_BASE_URL + "original" + poster + images["poster_small"] = IMAGE_BASE_URL + "w342" + poster + images["thumb"] = IMAGE_BASE_URL + "w342" + poster + if still: + images["thumb"] = IMAGE_BASE_URL + "w300" + still + images["still"] = IMAGE_BASE_URL + "w300" + still + images["still_original"] = IMAGE_BASE_URL + "original" + still + images["still_small"] = IMAGE_BASE_URL + "w185" + still + if fanart: + images["fanart"] = IMAGE_BASE_URL + "w1280" + fanart + images["fanart_original"] = IMAGE_BASE_URL + "original" + fanart + images["original"] = IMAGE_BASE_URL + "original" + fanart + images["fanart_small"] = IMAGE_BASE_URL + "w780" + fanart + if profile: + images["poster"] = IMAGE_BASE_URL + "w500" + profile + images["poster_original"] = IMAGE_BASE_URL + "original" + profile + images["poster_small"] = IMAGE_BASE_URL + "w342" + profile + images["thumb"] = IMAGE_BASE_URL + "w342" + profile + return images + + +def get_movie_tmdb_id(imdb_id:str=None, name:str=None, dbid:int=None): + """Gets tmdb id for movie + + + Args: + imdb_id (str, optional): the movie imbd id. Defaults to None. + name (str, optional): the movie title. Defaults to None. + dbid (int, optional): the kodi db movie id should be > 0. Defaults to None. + + Returns: + str: tmdb id for local movie get local imdb_id and use for query + or fall back to title + """ + if dbid and (int(dbid) > 0): + imdb_id = local_db.get_imdb_id("movie", dbid) + if imdb_id: + params = {"external_source": "imdb_id", + "language": addon.setting("LanguageID")} + response = get_data(url="find/tt%s" % (imdb_id.replace("tt", "")), + params=params) + if response and response["movie_results"]: + return response["movie_results"][0]["id"] + return search_media(media_name = name) if name else None + + +def get_show_tmdb_id(tvdb_id=None, source="tvdb_id"): + params = {"external_source": source, + "language": addon.setting("LanguageID")} + response = get_data(url="find/%s" % (tvdb_id), + params=params) + if not response or not response["tv_results"]: + utils.notify("TVShow Info not available.") + return None + return response["tv_results"][0]["id"] + + +def get_show_id(tmdb_id=None, return_id="imdb_id"): + params = {"append_to_response": "external_ids", + "language": addon.setting("LanguageID")} + response = get_data(url="tv/%s" % (tmdb_id), + params=params) + if not response: + utils.notify("TVShow Info not available.") + return None + return response["external_ids"][return_id] + + +def get_movie_videos(movie_id): + ''' + get trailers / teasers for movie with *movie_id + ''' + response = get_movie(movie_id) + if response and "videos" in response and response['videos']['results']: + return response['videos']['results'] + utils.notify("Could not get movie videos") + return None + + +def extended_movie_info(movie_id=None, dbid=None, cache_days=14) -> tuple[VideoItem, dict[str, ItemList], dict] | None: + """get listitem with extended info for movie with *movie_id + merge in info from *dbid if available + + Args: + movie_id (str, optional): TMDB movie id. Defaults to None. + dbid (int, optional): Local library dbid. Defaults to None. + cache_days (int, optional): Days to use cached info. Defaults to 14. + + Returns: + tuple: kutils131 VideoItem of movie info + dict of key str value kutils131 ItemList + dict of account states + """ + if not movie_id: + return None + info: dict | None = get_movie(movie_id=movie_id, cache_days=cache_days) + if not info or info.get('success') is False: + utils.notify("Could not get tmdb movie information") + return (None, None, None) + mpaa = "" + studio = [i["name"] for i in info.get("production_companies")] + authors = [i["name"] for i in info['credits'] + ['crew'] if i["department"] == "Writing"] + directors = [i["name"] for i in info['credits'] + ['crew'] if i["department"] == "Directing"] + us_cert = utils.dictfind( + info['release_dates']['results'], "iso_3166_1", "US") + if us_cert: + mpaa = us_cert['release_dates'][0]["certification"] + elif info['release_dates']['results']: + mpaa = info['release_dates']['results'][0]['release_dates'][0]['certification'] + movie_set:dict = info.get("belongs_to_collection") + movie = VideoItem(label=info.get('title'), + path=PLUGIN_BASE + 'youtubevideo&&id=%s' % info.get("id", "")) + movie.set_infos({'title': info.get('title'), + 'tagline': info.get('tagline'), + 'duration': info.get('runtime'), + 'mpaa': mpaa, + 'director': " / ".join(directors), + 'writer': " / ".join(authors), + 'plot': info.get('overview'), + 'originaltitle': info.get('original_title'), + 'country': info.get('original_language'), + 'imdbnumber': info.get('imdb_id'), + 'genre': " / ".join([i["name"] for i in info["genres"]]), + 'year': utils.get_year(info.get("release_date")), + 'rating': round(info['vote_average'], 1) if info.get('vote_average') else "", + 'premiered': info.get('release_date'), + 'votes': info.get('vote_count'), + 'studio': " / ".join(studio), + 'status': translate_status(info.get('status'))}) + movie.set_properties({'adult': str(info.get('adult')), + 'popularity': round(info['popularity'], 1) if info.get('popularity') else "", + 'set': movie_set.get("name") if movie_set else "", + 'set_id': movie_set.get("id") if movie_set else "", + 'id': info.get('id'), + 'imdb_id': info.get('imdb_id'), + 'budget': utils.millify(info.get("budget")), + 'revenue': utils.millify(info.get("revenue")), + 'homepage': info.get('homepage')}) + movie.set_artwork(get_image_urls(poster=info.get("poster_path"), + fanart=info.get("backdrop_path"))) + videos = handle_videos(info["videos"]["results"] + ) if "videos" in info else [] + account_states: dict = info.get("account_states") + if dbid: + local_item: dict = local_db.get_movie(dbid) + movie.update_from_listitem(local_item) + else: + movie = local_db.merge_with_local("movie", [movie])[0] + # hack to get tmdb rating instead of local one + movie.set_info("rating", round( + info['vote_average'], 1) if info.get('vote_average') else "") + releases = merge_with_cert_desc(handle_release_dates( + info["release_dates"]["results"]), "movie") + listitems: dict[str, ItemList] = {"actors": handle_people(info["credits"]["cast"]), + "similar": handle_movies(info["similar"]["results"]), + "lists": sort_lists(handle_lists(info["lists"]["results"])), + "studios": handle_companies(info["production_companies"]), + "releases": releases, + "crew": handle_people(info["credits"]["crew"]).reduce(), + "genres": handle_text(info["genres"]), + "keywords": handle_text(info["keywords"]["keywords"]), + "reviews": handle_reviews(info["reviews"]["results"]), + "videos": videos, + "images": handle_images(info["images"]["posters"]), + "backdrops": handle_images(info["images"]["backdrops"])} + return (movie, listitems, account_states) + + +def get_tvshow(tvshow_id, cache_days=30, light=False): + if not tvshow_id: + return None + params = {"append_to_response": None if light else ALL_TV_PROPS, + "language": addon.setting("LanguageID"), + "include_image_language": "en,null,%s" % addon.setting("LanguageID")} + if Login.check_login(): + params["session_id"] = Login.get_session_id() + return get_data(url="tv/%s" % (tvshow_id), + params=params, + cache_days=cache_days) + + +def extended_tvshow_info(tvshow_id=None, cache_days=7, dbid=None): + ''' + get listitem with extended info for tvshow with *tvshow_id + merge in info from *dbid if available + ''' + info = get_tvshow(tvshow_id, cache_days) + if not info: + return False + account_states = info.get("account_states") + videos = handle_videos(info["videos"]["results"] + ) if "videos" in info else [] + tmdb_id = info.get("id", "") + if len(info.get("episode_run_time", -1)) > 1: + duration = "%i - %i" % (min(info["episode_run_time"]), + max(info["episode_run_time"])) + elif len(info.get("episode_run_time", -1)) == 1: + duration = "%i" % (info["episode_run_time"][0]) + else: + duration = "" + mpaas = info['content_ratings']['results'] + us_cert = utils.dictfind(mpaas, "iso_3166_1", "US") + if us_cert: + mpaa = us_cert["rating"] + elif mpaas: + mpaa = mpaas[0]['rating'] + else: + mpaa = "" + tvshow = VideoItem(label=info.get('name'), + path=PLUGIN_BASE + 'extendedtvinfo&&id=%s' % tmdb_id) + tvshow.set_infos({'title': info.get('name'), + 'originaltitle': info.get('original_name', ""), + 'duration': duration, + 'mpaa': mpaa, + 'genre': " / ".join([i["name"] for i in info["genres"]]), + 'plot': info.get("overview"), + 'year': utils.get_year(info.get('first_air_date')), + 'mediatype': "tvshow", + 'rating': round(info['vote_average'], 1) if info.get('vote_average') else "", + 'country': info.get('original_language'), + 'userrating': info.get('rating'), + 'votes': info.get('vote_count'), + 'premiered': info.get('first_air_date'), + 'Status': translate_status(info.get('status'))}) + tvshow.set_properties({'credit_id': info.get('credit_id'), + 'id': tmdb_id, + 'popularity': round(info['popularity'], 1) if info.get('popularity') else "", + 'showtype': info.get('type'), + 'homepage': info.get('homepage'), + 'last_air_date': info.get('last_air_date'), + 'totalepisodes': info.get('number_of_episodes'), + 'totalseasons': info.get('number_of_seasons'), + 'in_production': info.get('in_production')}) + tvshow.set_artwork(get_image_urls(poster=info.get("poster_path"), + fanart=info.get("backdrop_path"))) + if dbid: + local_item = local_db.get_tvshow(dbid) + tvshow.update_from_listitem(local_item) + else: + tvshow = local_db.merge_with_local("tvshow", [tvshow])[0] + # hack to get tmdb rating instead of local one + tvshow.set_info("rating", round( + info['vote_average'], 1) if info.get('vote_average') else "") + certifications = merge_with_cert_desc( + handle_content_ratings(info["content_ratings"]["results"]), "tv") + listitems = {"actors": handle_people(info["credits"]["cast"]), + "similar": handle_tvshows(info["similar"]["results"]), + "studios": handle_companies(info["production_companies"]), + "networks": handle_companies(info["networks"]), + "certifications": certifications, + "crew": handle_people(info["credits"]["crew"]), + "genres": handle_text(info["genres"]), + "keywords": handle_text(info["keywords"]["results"]), + "videos": videos, + "seasons": handle_seasons(info["seasons"]), + "images": handle_images(info["images"]["posters"]), + "backdrops": handle_images(info["images"]["backdrops"])} + return (tvshow, listitems, account_states) + + +def extended_season_info(tvshow_id, season_number): + ''' + get listitem with extended info for season (*tvshow_id, *season_number) + ''' + if not tvshow_id or season_number is None: + return None + tvshow = get_tvshow(tvshow_id) + params = {"append_to_response": ALL_SEASON_PROPS, + "language": addon.setting("LanguageID"), + "include_image_language": "en,null,%s" % addon.setting("LanguageID")} + response = get_data(url="tv/%s/season/%s" % (tvshow_id, season_number), + params=params, + cache_days=7) + if not response: + utils.notify("Could not find season info") + return None + if response.get("name"): + title = response["name"] + elif season_number == "0": + title = addon.LANG(20381) + else: + title = "%s %s" % (addon.LANG(20373), season_number) + season = VideoItem(label=title) + season.set_infos({'plot': response["overview"], + 'tvshowtitle': tvshow.get('name'), + 'title': title, + 'premiered': response["air_date"]}) + season.set_artwork(get_image_urls(poster=response.get("poster_path"))) + season.set_properties({'id': response["id"]}) + videos = handle_videos( + response["videos"]["results"]) if "videos" in response else [] + listitems = {"actors": handle_people(response["credits"]["cast"]), + "crew": handle_people(response["credits"]["crew"]), + "videos": videos, + "episodes": handle_episodes(response["episodes"]), + "images": handle_images(response["images"]["posters"]), + "backdrops": handle_images(response["images"].get("backdrops", []))} + return (season, listitems) + + +def get_episode(tvshow_id, season, episode, cache_days=7): + if not tvshow_id or not episode: + return None + if not season: + season = 0 + params = {"append_to_response": ALL_EPISODE_PROPS, + "language": addon.setting("LanguageID"), + "include_image_language": "en,null,%s" % addon.setting("LanguageID")} + if Login.check_login(): + params["session_id"] = Login.get_session_id() + return get_data(url="tv/%s/season/%s/episode/%s" % (tvshow_id, season, episode), + params=params, + cache_days=cache_days) + + +def extended_episode_info(tvshow_id, season, episode, cache_days=7): + ''' + get ListItem and lists with extended info for episode (*tvshow_id, *season, *episode) + ''' + response = get_episode(tvshow_id, season, episode, cache_days) + if not response: + utils.notify("Could not find episode info") + return None + answer = {"actors": handle_people(response["credits"]["cast"] + response["credits"]["guest_stars"]), + "crew": handle_people(response["credits"]["crew"]), + "videos": handle_videos(response["videos"]["results"]) if "videos" in response else [], + "images": handle_images(response["images"]["stills"])} + return (handle_episodes([response])[0], answer, response.get("account_states")) + + +def extended_actor_info(actor_id: int) -> tuple[VideoItem, dict[str, ItemList]]: + """gets ListItem and lists with extended info for actor with actor_id + data is a dict from JSON returned by tmdb for an actor + lists is a dict of "append_to_response" queries extracted from data + info is a Kodi video listitem instance with properties set from data + + Args: + actor_id (int): the tmdb actor id + + Returns: + info[VideoItem]: a populated Kodi listitem + lists[dict]: a dict of kutils131 Itemlists (one per category) Itemlist is sequence + of kutils131 VideoItems + None: if no results from tmdb + """ + if not actor_id: + return None + data: dict = get_data(url="person/%s" % (actor_id), + params={"append_to_response": ALL_ACTOR_PROPS}, + cache_days=1) + if not data: + utils.notify("Could not find actor info") + return None + # extract info from data dict as list of ItemLists + lists: dict[str, ItemList] = {"movie_roles": handle_movies(data["movie_credits"]["cast"]).reduce("character"), + "tvshow_roles": handle_tvshows(data["tv_credits"]["cast"]).reduce("character"), + "movie_crew_roles": handle_movies(data["movie_credits"]["crew"]).reduce(), + "tvshow_crew_roles": handle_tvshows(data["tv_credits"]["crew"]).reduce(), + "tagged_images": handle_images(data["tagged_images"]["results"]) if "tagged_images" in data else [], + "images": handle_images(data["images"]["profiles"])} + info = VideoItem(label=data['name'], + path=f"{PLUGIN_BASE}extendedactorinfo&&id={data['id']}", + infos={'mediatype': "artist"}) # kutils VideoItem subclass of ListItem a kodi-like class + info.set_properties({'adult': data.get('adult'), + 'alsoknownas': " / ".join(data.get('also_known_as', [])), + 'biography': data.get('biography'), + 'birthday': data.get('birthday'), + 'age': utils.calculate_age(data.get('birthday'), data.get('deathday')), + 'character': data.get('character'), #cast + 'department': data.get('department'), #crew + 'job': data.get('job'), #crew + 'id': data['id'], + 'gender': GENDERS.get(data['gender']), + 'cast_id': data.get('cast_id'), + 'credit_id': data.get('credit_id'), #cast and crew + 'deathday': data.get('deathday'), + 'placeofbirth': data.get('place_of_birth'), + 'homepage': data.get('homepage'), + 'totalmovies': len(lists["movie_roles"]), + "DBMovies": len([d for d in lists["movie_roles"] if "dbid" in d])}) + info.set_artwork(get_image_urls(profile=data.get("profile_path"))) + return (info, lists) + + +def translate_status(status): + ''' + get movies from person with *person_id + ''' + return STATUS.get(status.lower(), status) + + +def get_movie_lists(movie_id) -> ItemList: + data = get_movie(movie_id) + return handle_lists(data["lists"]["results"]) + + +def get_rated_media_items(media_type, sort_by=None, page=1, cache_days=0): + ''' + takes "tv/episodes", "tv" or "movies" + ''' + if Login.check_login(): + session_id = Login.get_session_id() + account_id = Login.get_account_id() + if not session_id: + utils.notify("Could not get session id") + return [] + params = {"sort_by": sort_by, + "page": page, + "session_id": session_id, + "language": addon.setting("LanguageID")} + data = get_data(url="account/%s/rated/%s" % (account_id, media_type), + params=params, + cache_days=cache_days) + else: + session_id = Login.get_guest_session_id() + if not session_id: + utils.notify("Could not get session id") + return [] + params = {"language": addon.setting("LanguageID"), + "page": page} + data = get_data(url="guest_session/%s/rated/%s" % (session_id, media_type), + params=params, + cache_days=0) + if media_type == "tv/episodes": + itemlist = handle_episodes(data["results"]) + elif media_type == "tv": + itemlist = handle_tvshows(data["results"], False, None) + else: + itemlist = handle_movies(data["results"], False, None) + itemlist.set_totals(data["total_results"]) + itemlist.set_total_pages(data["total_pages"]) + return itemlist + + +def get_fav_items(media_type, sort_by=None, page=1): + ''' + takes "tv/episodes", "tv" or "movies" + ''' + session_id = Login.get_session_id() + account_id = Login.get_account_id() + if not session_id: + utils.notify("Could not get session id") + return [] + params = {"sort_by": sort_by, + "language": addon.setting("LanguageID"), + "page": page, + "session_id": session_id} + data = get_data(url="account/%s/favorite/%s" % (account_id, media_type), + params=params, + cache_days=0) + if "results" not in data: + return [] + if media_type == "tv": + itemlist = handle_tvshows(data["results"], False, None) + elif media_type == "tv/episodes": + itemlist = handle_episodes(data["results"]) + else: + itemlist = handle_movies(data["results"], False, None) + itemlist.set_totals(data["total_results"]) + itemlist.set_total_pages(data["total_pages"]) + return itemlist + + +def get_movies_from_list(list_id:str, cache_days=5): + ''' + get movie dict list from tmdb list. + ''' + data = get_data(url=f"list/{list_id}", + params={"language": addon.setting("LanguageID")}, + cache_days=cache_days) + return handle_movies(data["items"], False, None) if data else [] + + +def get_popular_actors(): + ''' + get dict list containing popular actors / directors / writers + ''' + response = get_data(url="person/popular", + cache_days=1) + return handle_people(response["results"]) + + +def get_actor_credits(actor_id, media_type): + ''' + media_type: movie or tv + ''' + response = get_data(url="person/%s/%s_credits" % (actor_id, media_type), + cache_days=1) + return handle_movies(response["cast"]) + + +def get_movie(movie_id, light=False, cache_days=30) -> dict | None: + """gets details from tmdb for a movie with tmdb movie-id + + Args: + movie_id (str): tmdb movie id + light (bool, optional): return limited info. Defaults to False. + cache_days (int, None):days to use cache vice new query. + Defaults to 30. + + Returns: + Union[dict, None]: A dict of movie infos. If no response from TMDB + returns None + """ + params = {"include_image_language": f"en,null,{addon.setting('LanguageID')}", + "language": addon.setting("LanguageID"), + "append_to_response": None if light else ALL_MOVIE_PROPS + } + if Login.check_login(): + params["session_id"] = Login.get_session_id() + return get_data(url=f"movie/{movie_id}", + params=params, + cache_days=cache_days) + +def get_similar_movies(movie_id): + ''' + get dict list containing movies similar to *movie_id + ''' + response = get_movie(movie_id) + if not response or not response.get("similar"): + return [] + return handle_movies(response["similar"]["results"]) + +def get_similar_tvshows(tvshow_id): + ''' + return list with similar tvshows for show with *tvshow_id (TMDB ID) + ''' + params = {"append_to_response": ALL_TV_PROPS, + "language": addon.setting("LanguageID"), + "include_image_language": "en,null,%s" % addon.setting("LanguageID")} + if Login.check_login(): + params["session_id"] = Login.get_session_id() + response = get_data(url="tv/%s" % (tvshow_id), + params=params, + cache_days=10) + if not response.get("similar"): + return [] + return handle_tvshows(response["similar"]["results"]) + + +def get_tvshows(tvshow_type): + ''' + return list with tv shows + available types: airing, on_the_air, top_rated, popular + ''' + response = get_data(url="tv/%s" % (tvshow_type), + params={"language": addon.setting("LanguageID")}, + cache_days=0.3) + if not response.get("results"): + return [] + return handle_tvshows(response["results"], False, None) + + +def get_movies(movie_type: str) -> list | dict: + """gets list with movies of movie_type from tmdb + + Args: + movie_type (str): now_playing, upcoming, top_rated, popular + + Returns: + list: [description] + """ + response = get_data(url=f'movie/{movie_type}', + params={"language": addon.setting("LanguageID")}, + cache_days=0.3) + if not response.get("results"): + return [] + return handle_movies(response["results"], False, None) + + +def get_set_movies(set_id:str) -> tuple[ItemList,dict]: + """Creates an ItemList of movie VideoItems for movies in set + + Args: + set_id (str): the tmdb collection id + + Returns: + tuple[ItemList,dict]: an ItemList of movies as VideoItems + a dict of set info + """ + params = {"append_to_response": "images", + "language": addon.setting("LanguageID"), + "include_image_language": "en,null,%s" % addon.setting("LanguageID")} + response = get_data(url="collection/%s" % (set_id), + params=params, + cache_days=14) + if not response: + return [], {} + artwork = get_image_urls(poster=response.get("poster_path"), + fanart=response.get("backdrop_path")) + info = {"label": response["name"], + "overview": response["overview"], + "id": response["id"]} + info.update(artwork) + return handle_movies(response.get("parts", [])), info + + +def get_person_movies(person_id): + ''' + get movies from person with *person_id + ''' + response = get_data(url="person/%s/credits" % (person_id), + params={"language": addon.setting("LanguageID")}, + cache_days=14) + # return handle_movies(response["crew"]) + handle_movies(response["cast"]) + if not response or "crew" not in response: + return [] + return handle_movies(response["crew"]) + + +def sort_lists(lists: ItemList) -> ItemList: + """sorts an itemlist by finding tmdb movie in account list + + Args: + lists (ItemList): an itemlist of movies + + Returns: + ItemList: the itemlist ordered by account list first + """ + if not Login.check_login(): + return lists + ids = [i["id"] for i in get_account_lists(10)] + own_lists = [i for i in lists if i.get_property("id") in ids] + for item in own_lists: + item.set_property("account", "True") + misc_lists = [i for i in lists if i.get_property("id") not in ids] + return own_lists + misc_lists + + +def search_media(media_name=None, year='', media_type="movie", cache_days=1): + ''' + return list of items with type *media_type for search with *media_name + ''' + if not media_name: + return None + params = {"query": "{} {}".format(media_name, year) if year else media_name, + "language": addon.setting("language"), + "include_adult": addon.setting("include_adults").lower()} + response = get_data(url="search/%s" % (media_type), + params=params, + cache_days=cache_days) + if response == "Empty": + return None + for item in response['results']: + if item['id']: + return item['id'] + + +Login = LoginProvider(username=addon.setting("tmdb_username"), + password=addon.setting("tmdb_password")) diff --git a/script.extendedinfo/resources/lib/trakt.py b/script.extendedinfo/resources/lib/trakt.py new file mode 100644 index 000000000..5148f67f6 --- /dev/null +++ b/script.extendedinfo/resources/lib/trakt.py @@ -0,0 +1,314 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details +"""Trakt module obtains data on TV Shows and Movies from Trakt.tv using +apiV2 If Trakt provides a TMDB id, additional data is retrieved from +TMDB + +Public functions: + get_episodes(content) gets upcoming episodes content shows or + premiering shows content premieres + returns a kutils131 ItemList + get_shows(show_type) gets tvshows for showtype trending/popular/anticipated + returns a kutils131 ItemList + get_shows_from_time(show_type, period) gets tvshos for showtype collected/played/ + watched for previous month + returns a kutils131 ItemList + get_movies(movie_type) gets movies for movietype trending/popular/anticipated + returns a kutils131 ItemList + get_movies_from_time(movie_type, period) gets movies forf movietype collected/ + played/watched for previous month + get_similar(media_type, imdb_id) gets related mediatype show(s)/movie(s) from + an imdb id. +""" + +import datetime +import urllib.error +import urllib.parse +import urllib.request + +from resources.kutil131 import ItemList, addon + +from resources.kutil131 import VideoItem, local_db, utils +from resources.lib import themoviedb as tmdb + +TRAKT_KEY = 'e9a7fba3fa1b527c08c073770869c258804124c5d7c984ce77206e695fbaddd5' +BASE_URL = "https://api.trakt.tv/" +HEADERS = { + 'Content-Type': 'application/json', + 'trakt-api-key': TRAKT_KEY, + 'trakt-api-version': '2' +} +PLUGIN_BASE = "plugin://script.extendedinfo/?info=" + + +def get_episodes(content): + """gets upcoming/premiering episodes from today + + Args: + content (str): enum shows (upcoming) or premieres (new shows) + + Returns: + ItemList: a kutils131 ItemList instance of VideoItems + """ + shows = ItemList(content_type="episodes") + url = "" + if content == "shows": + url = f'calendars/shows/{datetime.date.today()}/14' + elif content == "premieres": + url = f'calendars/shows/premieres/{datetime.date.today()}/14' + results = get_data(url=url, + params={"extended": "full"}, + cache_days=0.3) + count = 1 + if not results: + return None + #results is a dict. Each key is an ISO date string (day) with value as a + #list of episodes for that date (episode), episode is a dict with keys airs-at, + #episode (ep), and show (tv) Get the first 20 episodes and create an ItemList + #for each episode as VideoItem + for day in results.items(): + for episode in day[1]: #dict of episode + ep = episode["episode"] + tv = episode["show"] + title = ep["title"] if ep["title"] else "" + label = f'{tv["title"]} - {ep["season"]}x{ep["number"]}. {title}' + show = VideoItem(label=label, + path=f'{PLUGIN_BASE}extendedtvinfo&&tvdb_id={tv["ids"]["tvdb"]}') + show.set_infos({'title': title, + 'aired': ep["first_aired"], + 'season': ep["season"], + 'episode': ep["number"], + 'tvshowtitle': tv["title"], + 'mediatype': "episode", + 'year': tv.get("year"), + 'duration': tv["runtime"] * 60 if tv["runtime"] else "", + 'studio': tv["network"], + 'plot': tv["overview"], + 'country': tv["country"], + 'status': tv["status"], + 'trailer': tv["trailer"], + 'imdbnumber': ep["ids"]["imdb"], + 'rating': tv["rating"], + 'genre': " / ".join(tv["genres"]), + 'mpaa': tv["certification"]}) + show.set_properties({'tvdb_id': ep["ids"]["tvdb"], + 'id': ep["ids"]["tvdb"], + 'imdb_id': ep["ids"]["imdb"], + 'homepage': tv["homepage"]}) + if tv["ids"].get("tmdb"): + art_info = tmdb.get_tvshow(tv["ids"]["tmdb"], light=True) + if art_info: + show.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path", ""), + fanart=art_info.get("backdrop_path", ""))) + shows.append(show) + count += 1 + if count > 20: + break + if count > 20: + break + return shows + + +def handle_movies(results): + """helper function creates kutils131 VideoItems and adds to an ItemList + + Args: + results (list): a list of dicts, each dict is Trakt data for movie + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + movies = ItemList(content_type="movies") + path = 'extendedinfo&&id=%s' if addon.bool_setting( + "infodialog_onclick") else "playtrailer&&id=%s" + for i in results: + item = i["movie"] if "movie" in i else i + trailer = f'{PLUGIN_BASE}youtubevideo&&id={utils.extract_youtube_id(item["trailer"])}' + movie = VideoItem(label=item["title"], + path=PLUGIN_BASE + path % item["ids"]["tmdb"]) + movie.set_infos({'title': item["title"], + 'duration': item["runtime"] * 60 if item["runtime"] else "", + 'tagline': item["tagline"], + 'mediatype': "movie", + 'trailer': trailer, + 'year': item["year"], + 'mpaa': item["certification"], + 'plot': item["overview"], + 'imdbnumber': item["ids"]["imdb"], + 'premiered': item["released"], + 'rating': round(item["rating"], 1), + 'votes': item["votes"], + 'genre': " / ".join(item["genres"])}) + movie.set_properties({'id': item["ids"]["tmdb"], + 'imdb_id': item["ids"]["imdb"], + 'trakt_id': item["ids"]["trakt"], + 'watchers': item.get("watchers"), + 'language': item.get("language"), + 'homepage': item.get("homepage")}) + art_info = tmdb.get_movie(item["ids"]["tmdb"], light=True) + if art_info: + movie.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path"), + fanart=art_info.get("backdrop_path"))) + movies.append(movie) + movies = local_db.merge_with_local(media_type="movie", + items=movies, + library_first=False) + movies.set_sorts(["mpaa", "duration"]) + return movies + + +def handle_tvshows(results): + """helper function creates kutils131 VideoItems and adds to an ItemList + + Args: + results (list): a list of dicts, each dict is Trakt data for show + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + shows = ItemList(content_type="tvshows") + for i in results: + item = i["show"] if "show" in i else i + airs = item.get("airs", {}) + show = VideoItem(label=item["title"], + path=f'{PLUGIN_BASE}extendedtvinfo&&tvdb_id={item["ids"]["tvdb"]}') + show.set_infos({'mediatype': "tvshow", + 'title': item["title"], + 'duration': item["runtime"] * 60 if item["runtime"] else "", + 'year': item["year"], + 'premiered': item["first_aired"][:10], + 'country': item["country"], + 'rating': round(item["rating"], 1), + 'votes': item["votes"], + 'imdbnumber': item['ids']["imdb"], + 'mpaa': item["certification"], + 'trailer': item["trailer"], + 'status': item.get("status"), + 'studio': item["network"], + 'genre': " / ".join(item["genres"]), + 'plot': item["overview"]}) + show.set_properties({'id': item['ids']["tmdb"], + 'tvdb_id': item['ids']["tvdb"], + 'imdb_id': item['ids']["imdb"], + 'trakt_id': item['ids']["trakt"], + 'language': item["language"], + 'aired_episodes': item["aired_episodes"], + 'homepage': item["homepage"], + 'airday': airs.get("day"), + 'airshorttime': airs.get("time"), + 'watchers': item.get("watchers")}) + art_info = tmdb.get_tvshow(item["ids"]["tmdb"], light=True) + if art_info: + show.set_artwork(tmdb.get_image_urls(poster=art_info.get("poster_path"), + fanart=art_info.get("backdrop_path"))) + shows.append(show) + shows = local_db.merge_with_local(media_type="tvshow", + items=shows, + library_first=False) + shows.set_sorts(["mpaa", "duration"]) + return shows + + +def get_shows(show_type): + """gets Trakt full data for shows of enumerated type + + Args: + show_type (str): enum trending/popular/anticipated + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + results = get_data(url=f'shows/{show_type}', + params={"extended": "full"}) + return handle_tvshows(results) if results else [] + + +def get_shows_from_time(show_type, period="monthly"): + """gets Trakt full data for shows of enumerated type for enumerated period + + Args: + show_type (str): enum collected/played/watched + period (str, optional): enum daily/weekly/monthly/yearly/all Defaults to "monthly" + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + results = get_data(url=f'shows/{show_type}/{period}', + params={"extended": "full"}) + return handle_tvshows(results) if results else [] + + +def get_movies(movie_type): + """gets Trakt full data for movies of enumerated type + + Args: + movie_type (str): enum trending/popular/anticipated + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + results = get_data(url=f'movies/{movie_type}', + params={"extended": "full"}) + return handle_movies(results) if results else [] + + +def get_movies_from_time(movie_type, period="monthly"): + """gets Trakt full data for movies of enumerated type for enumerated period + + Args: + movie_type (str): enum collected/played/watched + period (str, optional): enum daily/weekly/monthly/yearly/all Defaults to "monthly" + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + results = get_data(url=f'movies/{movie_type}/{period}', + params={"extended": "full"}) + return handle_movies(results) if results else [] + + +def get_similar(media_type, imdb_id): + """gets related movies or shows from imbd id + + Args: + media_type (str): enum show/movie + imdb_id (str): the imbd id for show or movie + + Returns: + ItemList: a kutils131 ItemList of VideoItems + """ + if not imdb_id or not media_type: + return None + results = get_data(url=f'{media_type}s/{imdb_id}/related', + params={"extended": "full"}) + if not results: + return None + if media_type == "show": + return handle_tvshows(results) + elif media_type == "movie": + return handle_movies(results) + + +def get_data(url, params=None, cache_days=10): + """helper function builds query and formats result. First attempts to + retrieve data from local cache and then issues a ResT GET to the api if cache + data not available + + Args: + url (str): the url for GET operation on api + params (dict, optional): GET query (?) Defaults to None. + cache_days (int, optional): Max age of cached data before requesting new. + Defaults to 10. + + Returns: + dict: a dict from the deserialized JSON response from api or None + Note: kutils131 does not return the GET failure code (ie if not 200) + """ + params = params if params else {} + params["limit"] = 10 + url = f"{BASE_URL}{url}?{urllib.parse.urlencode(params)}" + return utils.get_JSON_response(url=url, + folder="Trakt", + headers=HEADERS, + cache_days=cache_days) diff --git a/script.extendedinfo/resources/lib/windowmanager.py b/script.extendedinfo/resources/lib/windowmanager.py new file mode 100644 index 000000000..23eb9b135 --- /dev/null +++ b/script.extendedinfo/resources/lib/windowmanager.py @@ -0,0 +1,309 @@ +# Copyright (C) 2015 - Philipp Temminghoff +# Modifications copyright (C) 2022 - Scott Smart +# This program is Free Software see LICENSE file for details + +# pylint: disable=line-too-long,import-outside-toplevel + +"""Module handles all actions to display dialogs +""" + +from __future__ import annotations + +import os +import re + +import xbmc +import xbmcgui +import xbmcvfs +from resources.kutil131 import addon, busy, player, windows + +from resources.kutil131 import local_db, utils +from resources.lib import themoviedb as tmdb + +INFO_XML_CLASSIC = f'script-{addon.ID}-DialogVideoInfo.xml' +LIST_XML_CLASSIC = f'script-{addon.ID}-VideoList.xml' +ACTOR_XML_CLASSIC = f'script-{addon.ID}-DialogInfo.xml' +if addon.bool_setting("force_native_layout") and addon.setting("xml_version") != addon.VERSION: + addon.set_setting("xml_version", addon.VERSION) + INFO_XML = f'script-{addon.ID}-DialogVideoInfo-classic.xml' + LIST_XML = f'script-{addon.ID}-VideoList-classic.xml' + ACTOR_XML = f'script-{addon.ID}-DialogInfo-classic.xml' + path = os.path.join(addon.PATH, "resources", "skins", "Default", "1080i") + xbmcvfs.copy(strSource=os.path.join(path, INFO_XML_CLASSIC), + strDestination=os.path.join(path, INFO_XML)) + xbmcvfs.copy(strSource=os.path.join(path, LIST_XML_CLASSIC), + strDestination=os.path.join(path, LIST_XML)) + xbmcvfs.copy(strSource=os.path.join(path, ACTOR_XML_CLASSIC), + strDestination=os.path.join(path, ACTOR_XML)) +else: + INFO_XML = INFO_XML_CLASSIC + LIST_XML = LIST_XML_CLASSIC + ACTOR_XML = ACTOR_XML_CLASSIC + + +class WindowManager: + """Class provides all operations to create/manage a Kodi dialog + window + + """ + window_stack = [] + + def __init__(self): + self.active_dialog = None + self.saved_background = addon.get_global("infobackground") + self.saved_control = xbmc.getInfoLabel("System.CurrentControlId") + self.saved_dialogstate = xbmc.getCondVisibility( + "Window.IsActive(Movieinformation)") + # self.monitor = SettingsMonitor() + self.monitor = xbmc.Monitor() + + def open_movie_info(self, movie_id:str=None, dbid:str=None, name:str=None, imdb_id:str=None): + """ + opens movie video info dialog, deal with window stack + """ + busy.show_busy() + from .dialogs.dialogmovieinfo import DialogMovieInfo + dbid = int(dbid) if dbid and int(dbid) > 0 else None + if not movie_id: + movie_id = tmdb.get_movie_tmdb_id(imdb_id=imdb_id, + dbid=dbid, + name=name) + dialog = DialogMovieInfo(INFO_XML, + addon.PATH, + id=movie_id, + dbid=dbid) + busy.hide_busy() + self.open_infodialog(dialog) + + def open_tvshow_info(self, tmdb_id=None, dbid=None, tvdb_id=None, imdb_id=None, name=None): + """ + open tvshow info, deal with window stack + """ + busy.show_busy() + dbid = int(dbid) if dbid and int(dbid) > 0 else None + from .dialogs.dialogtvshowinfo import DialogTVShowInfo + if tmdb_id: + pass + elif tvdb_id: + tmdb_id = tmdb.get_show_tmdb_id(tvdb_id) + elif imdb_id: + tmdb_id = tmdb.get_show_tmdb_id(tvdb_id=imdb_id, + source="imdb_id") + elif dbid: + tvdb_id = local_db.get_imdb_id(media_type="tvshow", + dbid=dbid) + if tvdb_id: + tmdb_id = tmdb.get_show_tmdb_id(tvdb_id) + elif name: + tmdb_id = tmdb.search_media(media_name=name, + year="", + media_type="tv") + dialog = DialogTVShowInfo(INFO_XML, + addon.PATH, + tmdb_id=tmdb_id, + dbid=dbid) + busy.hide_busy() + self.open_infodialog(dialog) + + def open_season_info(self, tvshow_id=None, season: int = None, tvshow=None, dbid=None): + """ + open season info, deal with window stack + needs *season AND (*tvshow_id OR *tvshow) + """ + busy.show_busy() + from .dialogs.dialogseasoninfo import DialogSeasonInfo + if not tvshow_id: + params = {"query": tvshow, + "language": addon.setting("language")} + response = tmdb.get_data(url="search/tv", + params=params, + cache_days=30) + if response["results"]: + tvshow_id = str(response['results'][0]['id']) + else: + params = {"query": re.sub(r'\(.*?\)', '', tvshow), + "language": addon.setting("language")} + response = tmdb.get_data(url="search/tv", + params=params, + cache_days=30) + if response["results"]: + tvshow_id = str(response['results'][0]['id']) + + dialog = DialogSeasonInfo(INFO_XML, + addon.PATH, + id=tvshow_id, + season=max(0, season), + dbid=int(dbid) if dbid and int(dbid) > 0 else None) + busy.hide_busy() + self.open_infodialog(dialog) + + def open_episode_info(self, tvshow_id=None, season=None, episode=None, tvshow=None, dbid=None): + """ + open season info, deal with window stack + needs (*tvshow_id OR *tvshow) AND *season AND *episode + """ + from .dialogs.dialogepisodeinfo import DialogEpisodeInfo + if not tvshow_id and tvshow: + tvshow_id = tmdb.search_media(media_name=tvshow, + media_type="tv", + cache_days=7) + dialog = DialogEpisodeInfo(INFO_XML, + addon.PATH, + tvshow_id=tvshow_id, + season=max(0, season), + episode=episode, + dbid=int(dbid) if dbid and int(dbid) > 0 else None) + self.open_infodialog(dialog) + + def open_actor_info(self, actor_id: int=None, name: str=None): + """opens info dialog window for an actor, deals with window stack + If a tmdb actor_id is passed, it is passed to a new dialog instance of + DialogActorInfo class. If actor name is passed, attempts to get the actor_id. + + Args: + actor_id (str, optional): tmdb actor id. Defaults to None. + name (str, optional): a string of name or name [separator name]*. + if name is a multiple a select dialog is presented to user to + get a single actor. If name is provided, attempts to get a tmdb + for it. Defaults to None. + + Returns: + None: if no tmdb actor id could be found + """ + from resources.lib.dialogs.dialogactorinfo import DialogActorInfo + if name and not actor_id: #use name to get person from tmdb for actor_id + name = name.split(f" {addon.LANG(20347)} ") + names = name[0].strip().split(" / ") + if len(names) > 1: + ret = xbmcgui.Dialog().select(heading=addon.LANG(32027), + list=names) #"Select person" + if ret == -1: + return None + name = names[ret] + else: + name = names[0] + busy.show_busy() + actor_info = tmdb.get_person_info(name) # a dict of info or False + # no TMDB info + if not actor_info: + busy.hide_busy() + return None + actor_id = actor_info["id"] + else: + busy.show_busy() + dialog = DialogActorInfo(ACTOR_XML, + addon.PATH, + id=actor_id) + busy.hide_busy() + self.open_infodialog(dialog) + + def open_video_list(self, listitems=None, filters=None, mode="filter", list_id=False, + filter_label="", force=False, media_type="movie", search_str=""): + """opens video list deals with window stack items + + Args: + listitems (dict, optional): [description]. Defaults to None. + filters (list, optional): [description]. Defaults to None. + mode (str, optional): [description]. Defaults to "filter". + list_id (bool, optional): [description]. Defaults to False. + filter_label (str, optional): [description]. Defaults to "". + force (bool, optional): [description]. Defaults to False. + media_type (str, optional): [description]. Defaults to "movie". + search_str (str, optional): [description]. Defaults to "". + """ + from .dialogs import dialogvideolist + Browser = dialogvideolist.get_window(windows.DialogXML) + dialog = Browser(LIST_XML, + addon.PATH, + listitems=listitems, + filters=[] if not filters else filters, + mode=mode, + list_id=list_id, + force=force, + filter_label=filter_label, + search_str=search_str, + type=media_type) + self.open_dialog(dialog) + + def open_youtube_list(self, search_str="", filters=None, filter_label="", media_type="video"): + """ + open video list, deal with window stack + """ + from .dialogs import dialogyoutubelist + YouTube = dialogyoutubelist.get_window(windows.DialogXML) + dialog = YouTube(f'script-{addon.ID}-YoutubeList.xml', + addon.PATH, + search_str=search_str, + filters=[] if not filters else filters, + type=media_type) + self.open_dialog(dialog) + + def open_infodialog(self, dialog): + """opens the info dialog + + Args: + dialog (DialogActorInfo | DialogMovieInfo | DialogTVShowInfo | DialogEpisodeInfo | DialogSeasonInfo): + a Dialog*Info instance of a Kodi dialog + dialog.info is a kutils131.VideoItem or AudioItem to display in dialog + """ + if dialog.info: + self.open_dialog(dialog) + else: + self.active_dialog = None + utils.notify(addon.LANG(32143)) #Could not find item at MovieDB + + def open_dialog(self, dialog): + """Opens a Kodi dialog managing a stack of dialogs + + Args: + dialog (DialogVideoList | DialogYoutubeList | Dialog*Info): a Kodi xml dialog window + """ + if self.active_dialog: + self.window_stack.append(self.active_dialog) + self.active_dialog.close() + utils.check_version() + if not addon.setting("first_start_infodialog"): + addon.set_setting("first_start_infodialog", "True") + xbmcgui.Dialog().ok(heading=addon.NAME, + message=addon.LANG(32140) + '[CR]' + addon.LANG(32141)) + self.active_dialog = dialog + try: + dialog.doModal() + except SystemExit: + pass +# if dialog.canceled: +# addon.set_global("infobackground", self.saved_background) +# self.window_stack = [] +# return None +# +# if self.window_stack and not self.monitor.abortRequested(): +# + if self.window_stack: + while not self.monitor.abortRequested() and player.started and not player.stopped: + self.monitor.waitForAbort(2) + self.active_dialog = self.window_stack.pop() + xbmc.sleep(300) + try: + self.active_dialog.doModal() + except SystemExit: + pass + else: + addon.set_global("infobackground", self.saved_background) + + def play_youtube_video(self, youtube_id="", listitem=None): + """ + play youtube vid with info from *listitem + """ + if self.active_dialog and self.active_dialog.window_type == "dialog": + self.active_dialog.close() + xbmc.executebuiltin("Dialog.Close(movieinformation)") + xbmc.executebuiltin("RunPlugin(plugin://plugin.video.youtube/play/?video_id=" + + youtube_id + "&screensaver=true&incognito=true)") + if self.active_dialog and self.active_dialog.window_type == "dialog": + player.wait_for_video_start() #30 sec timeout + player.wait_for_video_end() #method returns when video ends + if not self.monitor.abortRequested(): + self.active_dialog.doModal() + + +wm = WindowManager() diff --git a/script.extendedinfo/resources/settings.xml b/script.extendedinfo/resources/settings.xml index b8ff7336e..b44adf6f0 100644 --- a/script.extendedinfo/resources/settings.xml +++ b/script.extendedinfo/resources/settings.xml @@ -4,6 +4,18 @@ + + 0 + false + 35704479108606 + + false + + + 32172 + true + + 0 true @@ -14,6 +26,12 @@ false + + 0 + false + true + + 0 RunScript(script.extendedinfo,info=deletecache) @@ -52,6 +70,28 @@ 32174 + + 0 + false + + + true + + + 582 + + + + 0 + false + + + true + + + 560 + + diff --git a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogInfo.xml b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogInfo.xml index 6cb854087..99f9b2cfa 100644 --- a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogInfo.xml +++ b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogInfo.xml @@ -23,7 +23,7 @@ 0 0 - - + -5000 SetFocus(132) noop @@ -32,7 +32,7 @@ 0 0 - - + -5000 noop Control.HasFocus(20001) @@ -131,7 +131,7 @@ 25 FF999999 font12 - + !String.isEmpty(Window.Property(Birthday)) @@ -182,7 +182,7 @@ !String.isEmpty(Window.Property(Homepage)) - + 255 @@ -205,7 +205,7 @@ 150 buttons/button-fo.png buttons/button-nofo.png - + 55 diff --git a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogVideoInfo.xml b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogVideoInfo.xml index 054235dda..e7d75b482 100644 --- a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogVideoInfo.xml +++ b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-DialogVideoInfo.xml @@ -24,7 +24,7 @@ 0 0 - - + -5000 SetFocus(132) noop @@ -33,7 +33,7 @@ 0 0 - - + -5000 noop Control.HasFocus(20001) @@ -69,7 +69,7 @@ SetFocus(20000) buttons/button-fo.png buttons/button-nofo.png - + 55 @@ -200,10 +200,10 @@ 400 - - + icons/heart.png icons/heart.png - - + 0 0 280 @@ -265,7 +265,7 @@ 44 44 !String.IsEmpty(Window.Property(favorite)) - !String.IsEmpty(Window.Property(tmdb_logged_in)) + !String.IsEmpty(Window.Property(tmdb_logged_in)) + !String.IsEmpty(Window.Property(FavButton_Label)) !String.IsEqual(Window.Property(type),season) + !String.IsEqual(Window.Property(type),episode) @@ -290,7 +290,7 @@ 44 44 !String.IsEmpty(Window.Property(tmdb_logged_in)) - !String.IsEqual(Window.Property(type),season) + !String.IsEqual(Window.Property(type),episode) + !String.IsEqual(Window.Property(type),season) + !String.IsEqual(Window.Property(type),episode) + !String.IsEqual(Window.Property(type),tvshow) @@ -3030,7 +3030,7 @@ 1100 scale common/black.png - thumbs/homewidget_shadow.png + overlays/shadow.png 10 diff --git a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-VideoList.xml b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-VideoList.xml index f73ccf1d1..ff88ba62a 100644 --- a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-VideoList.xml +++ b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-VideoList.xml @@ -6,6 +6,7 @@ SetProperty(WindowColor,$VAR[AreaColorVar],home) --> ClearProperty(WindowColor,home) 500 + 9000 common/white.png @@ -92,13 +93,13 @@ 30 396 200 - common/black.png - 3 + common/black.png + 4 222 - scale + keep - 270 + 275 40 395 300 @@ -108,7 +109,7 @@ 40 - 320 + 255 135 26 Conditional @@ -120,7 +121,7 @@ 395 500 justify - + true font12 FFDDDDDD @@ -589,7 +590,7 @@ 25 font13 315 - + FFBBBBBB FFFAFAFA 200 @@ -619,7 +620,7 @@ [COLOR FFFAFAFA]$INFO[Window.Property(Type)][/COLOR] 438 - 70 + 55 40 315 left @@ -635,7 +636,7 @@ [COLOR FFFAFAFA]$INFO[Window.Property(Sort_Label)][/COLOR] 438 - 70 + 55 40 30 315 @@ -651,7 +652,7 @@ [COLOR FFFAFAFA]$INFO[Window.Property(Order_Label)][/COLOR] 438 - 70 + 55 40 30 315 @@ -786,6 +787,20 @@ FFBBBBBB FFFAFAFA + + + 438 + 55 + 40 + 315 + left + center + lists/focus.png + lists/separator.png + font12 + FFBBBBBB + FFFAFAFA + 438 diff --git a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-pictureviewer.xml b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-pictureviewer.xml new file mode 100644 index 000000000..38a3059a2 --- /dev/null +++ b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-pictureviewer.xml @@ -0,0 +1,74 @@ + + + 5000 + no + + + 0 + 0 + 1920 + 1080 + 500 + common/black.png + WindowOpen + WindowClose + + + 910 + 503 + 100 + 100 + !Container(5000).OnScrollPrevious + !Container(5000).OnScrollNext + Visible + Conditional + spinner.png + Conditional + + + 925 + 518 + 70 + 70 + !Container(5000).OnScrollPrevious + !Container(5000).OnScrollNext + Visible + Conditional + spinner.png + Conditional + + + -90 + 0 + 2100 + 1080 + 5000 + 5000 + 9001 + WindowOpen + WindowClose + 400 + Horizontal + + + 90 + 0 + 1920 + 1080 + keep + 200 + $INFO[ListItem.Art(original)] + + + + + 90 + 0 + 1920 + 1080 + keep + 200 + $INFO[ListItem.Art(original)] + + + + + diff --git a/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-t9search.xml b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-t9search.xml new file mode 100644 index 000000000..ad67dd13d --- /dev/null +++ b/script.extendedinfo/resources/skins/Default/1080i/script-script.extendedinfo-t9search.xml @@ -0,0 +1,166 @@ + + + 9090 + + + + + + + + + + + 20 + 0 + + -112 + 600 + 1080 + lists/panel.png + + + 15 + -31 + 447 + 708 + 9090 + Action(Close) + Control.SetFocus(9091,999) + Control.SetFocus(9091,0) + noop + vertical + 200 + + + 0 + 27 + 140 + 133 + buttons/button-nofo.png + + + 0 + 7 + 140 + 133 + center + center + font12 + + + + 0 + 37 + 140 + 133 + center + center + font10 + + + + + + 0 + 27 + 140 + 133 + buttons/button-fo.png + + + 0 + 7 + 140 + 133 + center + center + font12 + + + + 0 + 37 + 140 + 133 + center + center + font10 + + + + + + 15 + 500 + 390 + 708 + 9091 + Action(Close) + 9090 + 9090 + noop + vertical + 200 + + + 0 + -5 + 380 + 85 + buttons/button-nofo.png + + + 25 + -9 + 330 + 85 + center + center + font12 + + + + + + 0 + -5 + 380 + 85 + buttons/button-nofo.png + !Control.HasFocus(9091) + + + 0 + -5 + 380 + 85 + buttons/button-fo.png + Control.HasFocus(9091) + + + 25 + -9 + 330 + 85 + center + center + font12 + + + + + + 15 + 455 + 380 + 50 + center + 15 + white + true + 20 + + + + diff --git a/script.extendedinfo/resources/skins/Default/media/DefaultVideo.png b/script.extendedinfo/resources/skins/Default/media/DefaultVideo.png new file mode 100644 index 000000000..e9f633d3d Binary files /dev/null and b/script.extendedinfo/resources/skins/Default/media/DefaultVideo.png differ diff --git a/script.extendedinfo/resources/skins/Default/media/flags/audio/opus.png b/script.extendedinfo/resources/skins/Default/media/flags/audio/opus.png new file mode 100644 index 000000000..df856a6d5 Binary files /dev/null and b/script.extendedinfo/resources/skins/Default/media/flags/audio/opus.png differ diff --git a/script.kodi.android.update/addon.xml b/script.kodi.android.update/addon.xml index f814e85b3..d97d66e15 100644 --- a/script.kodi.android.update/addon.xml +++ b/script.kodi.android.update/addon.xml @@ -1,5 +1,5 @@ - + @@ -27,26 +27,33 @@ Hent og installer Kodi-installationspakker. Kodi-Pakete herunterladen und installieren. Download and Install Kodi packages. + Descargar e instalar los paquetes de Kodi. Descarga e instala paquetes de Kodi. Laadi alla ja paigalda Kodi pakette. Lataa ja asenna Kodi-paketteja. Télécharger et installer les packages Kodi. Preuzmi i instaliraj Kodi pakete. + Töltsd le és telepítsd a Kodi csomagot. Scarica e installa i pacchetti Kodi. Kodi 패키지를 다운로드하고 설치합니다. Pobieraj i instaluj pakiety Kodi. + Baixe e instale pacotes Kodi. Скачиваете и устанавливайте пакеты Kodi. 下载并安装 Kodi 软件包。 Stáhnout a nainstalovat Kodi android balíčky; vyberte z Nightlies, Releases, Snapshots and Test-builds. Download og installer Android eksekverbare filer til Kodi; vælg fra Nightlies, Udgivelser, Snapshots og Test-builds. Kodi-Pakete für Android herunterladen und installieren. Auswählen zwischen Nightlies, Releases, Snapshopts und Test-Builds. Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds. + Descargar e instalar los paquetes android de Kodi; seleccionar entre Nightlies, Releases, Snapshots y Test-builds. Laadi alla ja paigalda Kodi android pakette; valikus on Nightlies, Releases, Snapshots ja Test-builds. Lataa ja asenna Kodin Android-paketteja kehityshaaroista Release, Nightly, Snapshot ja Test. + Télécharger et installer les packages Android de Kodi ; sélectionner depuis Nightlies, Releases, Snapshots et Test-builds. Preuzmi i instaliraj Kodi android pakete; odaberite od Nightlies, Releases, Snapshots i Testnih-izgradnji. - Scarica e installa i pacchetti Android Kodi; seleziona da Nightlies, Release, Snapshot e Test build. + Töltsd le és telepítsd a Kodi android csomagokat; válassz a Nightlyk, Stabil, Speciális és Teszt buildek közül. + Scarica e installa i pacchetti Android di Kodi; seleziona da Nightlies, Release, Snapshot e Test-build. Kodi 안드로이드 패키지를 다운로드하고 설치하십시오. 매일판, 릴리스, 스냅샷 및 테스트 빌드에서 선택합니다. Pobieraj i instaluj pliki wykonywalne Kodi: kompilacje nocne, testowe, wydania finalne, migawki. + Baixe e instale pacotes Android Kodi; selecione entre · · Nightlies, Releases, Snapshots e Test-builds. Скачивание и установка Kodi пакетов для Android; выбор из Ночных, Релизных, Тестовых сборок и Снапшотов. 下载并安装 Kodi android 软件包。从 Nightlies、Releases、Snapshots 和 Test-builds 中选择。
diff --git a/script.kodi.android.update/default.py b/script.kodi.android.update/default.py index 72f143c29..e705016f5 100644 --- a/script.kodi.android.update/default.py +++ b/script.kodi.android.update/default.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023 Team-Kodi +# Copyright (C) 2024 Team-Kodi # # This file is part of script.kodi.android.update # @@ -34,14 +34,14 @@ CLEAN = REAL_SETTINGS.getSetting('Disable_Maintenance') == 'false' VERSION = REAL_SETTINGS.getSetting("Version") #VERSION = 'Android 4.0.0 API level 24, kernel: Linux ARM 64-bit version 3.10.96+' #Test BASE_URL = 'http://mirrors.kodi.tv/' -BRANCHS = {21:'omega',20:'nexus',19:'matrix',18:'leia',17:'krypton',16:'jarvis',15:'isengard',14:'helix',13:'gotham','':''} +BRANCHS = {22:'piers',21:'omega',20:'nexus',19:'matrix',18:'leia',17:'krypton',16:'jarvis',15:'isengard',14:'helix',13:'gotham','':''} BUILD_OPT = {'nightlies':LANGUAGE(30017),'releases':LANGUAGE(30016),'snapshots':LANGUAGE(30015),'test-builds':LANGUAGE(30018)} try: BUILD = json.loads(REAL_SETTINGS.getSetting("Build")) BRANCH = BRANCHS[int(BUILD.get('major',''))] except: - BUILD = '' + BUILD = '' BRANCH = 'master' DROID_URL = BASE_URL + '%s/android/%s/' @@ -50,10 +50,10 @@ CUSTOM = (REAL_SETTINGS.getSetting('Custom_Manager') or 'com.android.documentsui') FMANAGER = {0:'com.android.documentsui',1:CUSTOM}[int(REAL_SETTINGS.getSetting('File_Manager'))] -if DEVICESTR is None: PLATFORM = "" +if DEVICESTR is None: PLATFORM = "" elif '64' in DEVICESTR: PLATFORM = "arm64-v8a" elif '86' in DEVICESTR: PLATFORM = "x86" -else: PLATFORM = "arm" +else: PLATFORM = "arm" def log(msg, level=xbmc.LOGDEBUG): if DEBUG == False and level != xbmc.LOGERROR: return @@ -126,7 +126,7 @@ def getItems(self, soup): def buildMain(self): tmpLST = [] - for label in sorted(BUILD_OPT.keys()): + for label in sorted(BUILD_OPT.keys()): liz = xbmcgui.ListItem(label.title(),BUILD_OPT[label],path=DROID_URL%(label,PLATFORM)) liz.setArt({'icon':ICON,'thumb':ICON}) tmpLST.append(liz) @@ -142,9 +142,10 @@ def buildItems(self, url): for item in self.getItems(soup): try: #folders label, label2 = re.compile("(.*?)/-(.*)").match(item).groups() - if label == PLATFORM: label2 = LANGUAGE(30014)%PLATFORM + print('buildItems',label,label2) + if label == PLATFORM: label2 = LANGUAGE(30014)%PLATFORM elif label.lower() == BRANCH.lower(): label2 = LANGUAGE(30022)%(BUILD.get('major',''),BUILD.get('minor',''),BUILD.get('revision','')) - else: label2 = '' #Don't use time-stamp for folders + else: label2 = '' #Don't use time-stamp for folders liz = xbmcgui.ListItem(label.title(),label2,path=(url + label)) liz.setArt({'icon':ICON,'thumb':ICON}) yield liz @@ -166,7 +167,7 @@ def selectPath(self, url, bypass=False): newURL = url while not self.myMonitor.abortRequested(): items = list(self.buildItems(url)) - if len(items) == 0: break + if len(items) == 0: break elif len(items) == 2 and not bypass and items[0].getLabel().lower() == 'parent directory' and not items[1].getLabel().startswith('.apk'): select = 1 #If one folder bypass selection. else: select = selectDialog(url.replace(BASE_URL,'./').replace('//','/'), items) if select is None: return #return on cancel. diff --git a/script.kodi.android.update/resources/language/resource.language.de_de/strings.po b/script.kodi.android.update/resources/language/resource.language.de_de/strings.po index 99ea28cd4..994c05954 100644 --- a/script.kodi.android.update/resources/language/resource.language.de_de/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.de_de/strings.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: script.kodi.android.update\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-02-14 08:13+0000\n" -"Last-Translator: Kai Sommerfeld \n" +"PO-Revision-Date: 2023-02-01 16:15+0000\n" +"Last-Translator: Demian \n" "Language-Team: German \n" "Language: de_de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.10.1\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." @@ -27,11 +27,11 @@ msgstr "Kodi-Pakete für Android herunterladen und installieren. Auswählen zwis msgctxt "#30000" msgid "Enable Debugging [Log errors]" -msgstr "Debugging erlauben [Protokoll-Fehler]" +msgstr "Debugging einschalten [Fehler protokollieren]" msgctxt "#30001" msgid "Something went wrong, Try again later..." -msgstr "Etwas ist schief gelaufen, erneut versuchen..." +msgstr "Etwas ist schief gelaufen, später erneut versuchen …" msgctxt "#30002" msgid "Preparing to download: %s" @@ -79,7 +79,7 @@ msgstr "Soll das Plugin deaktiviert werden?" msgctxt "#30013" msgid "Detecting Android version..." -msgstr "Android-Version erkennen..." +msgstr "Android-Version erkennen …" msgctxt "#30014" msgid "Current Android platform (%s)" @@ -91,7 +91,7 @@ msgstr "Semi-stabil (Monatliche Builds)." msgctxt "#30016" msgid "Stable (Final releases)." -msgstr "Stabil (Finale Veröffentlichungen)." +msgstr "Stabil (Endgültige Version)." msgctxt "#30017" msgid "Unstable (Daily builds)." @@ -115,7 +115,7 @@ msgstr "Datei-Explorer" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "Aktueller Kodi-Branch (%s.%s-%s)" +msgstr "Aktueller Kodi-Zweig (%s.%s-%s)" msgctxt "#30023" msgid "[B]Downloading:[/B]" @@ -123,8 +123,8 @@ msgstr "[B]Wird heruntergeladen:[/B]" msgctxt "#30024" msgid " | [B]Speed:[/B]" -msgstr " -| [B]Geschwindigkeit:[/B]" +msgstr " | [B]Geschwindigkeit:[/B]" msgctxt "#30025" msgid " | [B]ETA:[/B]" -msgstr " -| [B]ETA:[/B]" +msgstr " | [B]ETA:[/B]" diff --git a/script.kodi.android.update/resources/language/resource.language.es_es/strings.po b/script.kodi.android.update/resources/language/resource.language.es_es/strings.po index 13815a2c9..651f10753 100644 --- a/script.kodi.android.update/resources/language/resource.language.es_es/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.es_es/strings.po @@ -7,124 +7,124 @@ msgstr "" "Project-Id-Version: script.kodi.android.update\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-03-01 16:01+0000\n" -"Last-Translator: Christian Gade \n" +"PO-Revision-Date: 2023-02-23 10:28+0000\n" +"Last-Translator: José Antonio Alvarado \n" "Language-Team: Spanish (Spain) \n" "Language: es_es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.11\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." -msgstr "" +msgstr "Descargar e instalar los paquetes de Kodi." msgctxt "Addon Description" msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" +msgstr "Descargar e instalar los paquetes android de Kodi; seleccionar entre Nightlies, Releases, Snapshots y Test-builds." msgctxt "#30000" msgid "Enable Debugging [Log errors]" -msgstr "Habilitar debugging [Registrar errores]" +msgstr "Activar depuración [Registrar errores]" msgctxt "#30001" msgid "Something went wrong, Try again later..." -msgstr "" +msgstr "Algo salió mal, inténtalo de nuevo más tarde..." msgctxt "#30002" msgid "Preparing to download: %s" -msgstr "" +msgstr "Preparándose para descargar: %s" msgctxt "#30003" msgid "Keep all downloads?" -msgstr "" +msgstr "¿Mantener todas las descargas?" msgctxt "#30004" msgid "File already exists" -msgstr "" +msgstr "El fichero ya existe" msgctxt "#30005" msgid "Install" -msgstr "" +msgstr "Instalar" msgctxt "#30006" msgid "Redownload" -msgstr "" +msgstr "Volver a descargar" msgctxt "#30007" msgid "Installation complete, file removed" -msgstr "" +msgstr "Instalación finalizada, archivo eliminado" msgctxt "#30008" msgid "Cleanup download after installation" -msgstr "" +msgstr "Limpiar la descarga tras la instalación" msgctxt "#30009" msgid "Plugin disabled" -msgstr "" +msgstr "Complemento desactivado" msgctxt "#30010" msgid "Unknown Android version" -msgstr "" +msgstr "Versión de Android desconocida" msgctxt "#30011" msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" +msgstr "Android v.%s.0.0 es incompatible con las nuevas versiones de Kodi," msgctxt "#30012" msgid "Would you like to disable this plugin?" -msgstr "" +msgstr "¿Desea desactivar este complemento?" msgctxt "#30013" msgid "Detecting Android version..." -msgstr "" +msgstr "Detectando la versión de Android..." msgctxt "#30014" msgid "Current Android platform (%s)" -msgstr "" +msgstr "Plataforma Android actual (%s)" msgctxt "#30015" msgid "Semi-stable (Monthly builds)." -msgstr "" +msgstr "Semiestable (compilaciones mensuales)." msgctxt "#30016" msgid "Stable (Final releases)." -msgstr "" +msgstr "Estable (versiones finales)." msgctxt "#30017" msgid "Unstable (Daily builds)." -msgstr "" +msgstr "Inestable (compilaciones diarias)." msgctxt "#30018" msgid "Experimental (Debugging builds)." -msgstr "" +msgstr "Experimental (versiones de depuración)." msgctxt "#30019" msgid "Incompatible platform" -msgstr "" +msgstr "Plataforma incompatible" msgctxt "#30020" msgid "Select File Explorer" -msgstr "" +msgstr "Seleccionar Explorador de archivos" msgctxt "#30021" msgid "File Explorer" -msgstr "" +msgstr "Explorador de archivos" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "" +msgstr "Rama actual de Kodi (%s.%s-%s)" msgctxt "#30023" msgid "[B]Downloading:[/B]" -msgstr "" +msgstr "[B]Descargando:[/B]" msgctxt "#30024" msgid " | [B]Speed:[/B]" -msgstr "" +msgstr " | [B]Velocidad:[/B]" msgctxt "#30025" msgid " | [B]ETA:[/B]" -msgstr "" +msgstr " | [B]ETA:[/B]" diff --git a/script.kodi.android.update/resources/language/resource.language.fr_fr/strings.po b/script.kodi.android.update/resources/language/resource.language.fr_fr/strings.po index d94faab94..b9d8e0139 100644 --- a/script.kodi.android.update/resources/language/resource.language.fr_fr/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.fr_fr/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: script.kodi.android.update\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2023-01-01 16:15+0000\n" +"PO-Revision-Date: 2023-07-18 17:11+0000\n" "Last-Translator: skypichat \n" "Language-Team: French (France) \n" "Language: fr_fr\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.15\n" +"X-Generator: Weblate 4.18.2\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." @@ -23,7 +23,7 @@ msgstr "Télécharger et installer les packages Kodi." msgctxt "Addon Description" msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" +msgstr "Télécharger et installer les packages Android de Kodi ; sélectionner depuis Nightlies, Releases, Snapshots et Test-builds." msgctxt "#30000" msgid "Enable Debugging [Log errors]" @@ -35,7 +35,7 @@ msgstr "Quelque chose s'est mal passé, réessayez plus tard..." msgctxt "#30002" msgid "Preparing to download: %s" -msgstr "" +msgstr "Téléchargement en préparation : %s" msgctxt "#30003" msgid "Keep all downloads?" @@ -55,7 +55,7 @@ msgstr "Retélécharger" msgctxt "#30007" msgid "Installation complete, file removed" -msgstr "" +msgstr "Installation terminée, fichier supprimé" msgctxt "#30008" msgid "Cleanup download after installation" @@ -67,11 +67,11 @@ msgstr "Plugin désactivé" msgctxt "#30010" msgid "Unknown Android version" -msgstr "" +msgstr "Version d'Android inconnue" msgctxt "#30011" msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" +msgstr "Android v.%s.0.0 est incompatible avec les nouvelles versions de Kodi," msgctxt "#30012" msgid "Would you like to disable this plugin?" @@ -79,11 +79,11 @@ msgstr "Souhaitez-vous désactiver ce plugin ?" msgctxt "#30013" msgid "Detecting Android version..." -msgstr "" +msgstr "Détection de la version d'Android..." msgctxt "#30014" msgid "Current Android platform (%s)" -msgstr "" +msgstr "Plateforme Android courante (%s)" msgctxt "#30015" msgid "Semi-stable (Monthly builds)." @@ -107,11 +107,11 @@ msgstr "Plateforme incompatible" msgctxt "#30020" msgid "Select File Explorer" -msgstr "" +msgstr "Sélectionner l'explorateur de fichiers" msgctxt "#30021" msgid "File Explorer" -msgstr "" +msgstr "Explorateur de fichiers" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" diff --git a/script.kodi.android.update/resources/language/resource.language.hu_hu/strings.po b/script.kodi.android.update/resources/language/resource.language.hu_hu/strings.po index 41bcab357..6da08a17d 100644 --- a/script.kodi.android.update/resources/language/resource.language.hu_hu/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.hu_hu/strings.po @@ -5,125 +5,126 @@ msgid "" msgstr "" "Project-Id-Version: script.kodi.android.update\n" -"Report-Msgid-Bugs-To: https://forum.kodi.tv/\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2024-02-05 14:02+0000\n" +"Last-Translator: Frodo19 \n" +"Language-Team: Hungarian \n" "Language: hu_hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.3\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." -msgstr "" +msgstr "Töltsd le és telepítsd a Kodi csomagot." msgctxt "Addon Description" msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" +msgstr "Töltsd le és telepítsd a Kodi android csomagokat; válassz a Nightlyk, Stabil, Speciális és Teszt buildek közül." msgctxt "#30000" msgid "Enable Debugging [Log errors]" -msgstr "" +msgstr "Debug engedélyezése [Log hibák]" msgctxt "#30001" msgid "Something went wrong, Try again later..." -msgstr "" +msgstr "Hiba történt, próbálkozz újra később..." msgctxt "#30002" msgid "Preparing to download: %s" -msgstr "" +msgstr "Felkészülés a letöltésre: %s" msgctxt "#30003" msgid "Keep all downloads?" -msgstr "" +msgstr "Megtartod az összes letöltést ?" msgctxt "#30004" msgid "File already exists" -msgstr "" +msgstr "A fájlok már léteznek" msgctxt "#30005" msgid "Install" -msgstr "" +msgstr "Telepítés" msgctxt "#30006" msgid "Redownload" -msgstr "" +msgstr "Letöltés ismét" msgctxt "#30007" msgid "Installation complete, file removed" -msgstr "" +msgstr "A telepítés befejeződött, a fájl eltávolítva" msgctxt "#30008" msgid "Cleanup download after installation" -msgstr "" +msgstr "Letöltött fájl törlése a telepítés után" msgctxt "#30009" msgid "Plugin disabled" -msgstr "" +msgstr "Plugin letiltva" msgctxt "#30010" msgid "Unknown Android version" -msgstr "" +msgstr "Ismeretlen android verzió" msgctxt "#30011" msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" +msgstr "Az Android v.%s.0.0 nem kompatibilis a Kodi új verzióival," msgctxt "#30012" msgid "Would you like to disable this plugin?" -msgstr "" +msgstr "Szeretnéd letiltani ezt a bővítményt?" msgctxt "#30013" msgid "Detecting Android version..." -msgstr "" +msgstr "Android verzió meghatározása..." msgctxt "#30014" msgid "Current Android platform (%s)" -msgstr "" +msgstr "Jelenlegi Android platform (%s)" msgctxt "#30015" msgid "Semi-stable (Monthly builds)." -msgstr "" +msgstr "Félig stabil (havi buildok)." msgctxt "#30016" msgid "Stable (Final releases)." -msgstr "" +msgstr "Stabil (Felhasználói verziók)." msgctxt "#30017" msgid "Unstable (Daily builds)." -msgstr "" +msgstr "Nem stabil (napi verziók)." msgctxt "#30018" msgid "Experimental (Debugging builds)." -msgstr "" +msgstr "Kísérleti (Hibakereső verziók)." msgctxt "#30019" msgid "Incompatible platform" -msgstr "" +msgstr "Nem kompatibilis platform" msgctxt "#30020" msgid "Select File Explorer" -msgstr "" +msgstr "Fájlkezelő kiválasztása" msgctxt "#30021" msgid "File Explorer" -msgstr "" +msgstr "Fáljkezelő" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "" +msgstr "Jelenlegi Kodi (%s.%s-%s)" msgctxt "#30023" msgid "[B]Downloading:[/B]" -msgstr "" +msgstr "[B]Letöltés:[/B]" msgctxt "#30024" msgid " | [B]Speed:[/B]" -msgstr "" +msgstr " | [B]Sebesség:[/B]" msgctxt "#30025" msgid " | [B]ETA:[/B]" -msgstr "" +msgstr " | [B]Fennmaradó:[/B]" diff --git a/script.kodi.android.update/resources/language/resource.language.it_it/strings.po b/script.kodi.android.update/resources/language/resource.language.it_it/strings.po index 2c559cfa5..01aecbbe1 100644 --- a/script.kodi.android.update/resources/language/resource.language.it_it/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.it_it/strings.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: script.kodi.android.update\n" "Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: 2022-10-03 10:15+0000\n" +"PO-Revision-Date: 2023-04-17 18:16+0000\n" "Last-Translator: Massimo Pissarello \n" "Language-Team: Italian \n" "Language: it_it\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.14.1\n" +"X-Generator: Weblate 4.15.2\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." @@ -23,11 +23,11 @@ msgstr "Scarica e installa i pacchetti Kodi." msgctxt "Addon Description" msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "Scarica e installa i pacchetti Android Kodi; seleziona da Nightlies, Release, Snapshot e Test build." +msgstr "Scarica e installa i pacchetti Android di Kodi; seleziona da Nightlies, Release, Snapshot e Test-build." msgctxt "#30000" msgid "Enable Debugging [Log errors]" -msgstr "Abilita debug [Registra errori]" +msgstr "Abilita debug [registra errori]" msgctxt "#30001" msgid "Something went wrong, Try again later..." @@ -39,7 +39,7 @@ msgstr "Preparazione per download: %s" msgctxt "#30003" msgid "Keep all downloads?" -msgstr "Conservare tutti i download?" +msgstr "Mantenere tutti i download?" msgctxt "#30004" msgid "File already exists" @@ -87,7 +87,7 @@ msgstr "Piattaforma Android attuale (%s)" msgctxt "#30015" msgid "Semi-stable (Monthly builds)." -msgstr "Semistabile (build mensili)." +msgstr "Semi-stabile (build mensili)." msgctxt "#30016" msgid "Stable (Final releases)." @@ -99,7 +99,7 @@ msgstr "Instabile (build giornaliere)." msgctxt "#30018" msgid "Experimental (Debugging builds)." -msgstr "Sperimentale (debug build)." +msgstr "Sperimentale (build di debug)." msgctxt "#30019" msgid "Incompatible platform" @@ -115,11 +115,11 @@ msgstr "Esplora file" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "Branch Kodi attuale (%s.%s-%s)" +msgstr "Branch attuale Kodi (%s.%s-%s)" msgctxt "#30023" msgid "[B]Downloading:[/B]" -msgstr "[B]Download:[/B]" +msgstr "[B]Download in corso:[/B]" msgctxt "#30024" msgid " | [B]Speed:[/B]" diff --git a/script.kodi.android.update/resources/language/resource.language.pt_br/strings.po b/script.kodi.android.update/resources/language/resource.language.pt_br/strings.po index c1e7c3707..c38f206da 100644 --- a/script.kodi.android.update/resources/language/resource.language.pt_br/strings.po +++ b/script.kodi.android.update/resources/language/resource.language.pt_br/strings.po @@ -5,124 +5,125 @@ msgid "" msgstr "" "Project-Id-Version: script.kodi.android.update\n" -"Report-Msgid-Bugs-To: https://forum.kodi.tv/\n" +"Report-Msgid-Bugs-To: translations@kodi.tv\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2024-05-14 17:28+0000\n" +"Last-Translator: Havok Dan \n" +"Language-Team: Portuguese (Brazil) \n" "Language: pt_br\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.5.4\n" msgctxt "Addon Summary" msgid "Download and Install Kodi packages." -msgstr "" +msgstr "Baixe e instale pacotes Kodi." msgctxt "Addon Description" msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" +msgstr "Baixe e instale pacotes Android Kodi; selecione entre · · Nightlies, Releases, Snapshots e Test-builds." msgctxt "#30000" msgid "Enable Debugging [Log errors]" -msgstr "" +msgstr "Habilitar depuração [erros de log]" msgctxt "#30001" msgid "Something went wrong, Try again later..." -msgstr "" +msgstr "Algo deu errado, tente novamente mais tarde..." msgctxt "#30002" msgid "Preparing to download: %s" -msgstr "" +msgstr "Preparando para baixar: %s" msgctxt "#30003" msgid "Keep all downloads?" -msgstr "" +msgstr "Manter todos os downloads?" msgctxt "#30004" msgid "File already exists" -msgstr "" +msgstr "O arquivo já existe" msgctxt "#30005" msgid "Install" -msgstr "" +msgstr "Instalar" msgctxt "#30006" msgid "Redownload" -msgstr "" +msgstr "Baixar novamente" msgctxt "#30007" msgid "Installation complete, file removed" -msgstr "" +msgstr "Instalação concluída, arquivo removido" msgctxt "#30008" msgid "Cleanup download after installation" -msgstr "" +msgstr "Limpar Download após a instalação" msgctxt "#30009" msgid "Plugin disabled" -msgstr "" +msgstr "Plugin desativado" msgctxt "#30010" msgid "Unknown Android version" -msgstr "" +msgstr "Versão Desconhecida do Android" msgctxt "#30011" msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" +msgstr "Android v.%s.0.0 é incompatível com novas versões do Kodi," msgctxt "#30012" msgid "Would you like to disable this plugin?" -msgstr "" +msgstr "Gostaria de desabilitar este plugin?" msgctxt "#30013" msgid "Detecting Android version..." -msgstr "" +msgstr "Detectando Versão do Android..." msgctxt "#30014" msgid "Current Android platform (%s)" -msgstr "" +msgstr "Plataforma Android atual (%s)" msgctxt "#30015" msgid "Semi-stable (Monthly builds)." -msgstr "" +msgstr "Semi-estável (Compilações mensais)." msgctxt "#30016" msgid "Stable (Final releases)." -msgstr "" +msgstr "Estável (versões finais)." msgctxt "#30017" msgid "Unstable (Daily builds)." -msgstr "" +msgstr "Instável (compilações diárias)." msgctxt "#30018" msgid "Experimental (Debugging builds)." -msgstr "" +msgstr "Experimental (compilações de depuração)." msgctxt "#30019" msgid "Incompatible platform" -msgstr "" +msgstr "Plataforma incompatível" msgctxt "#30020" msgid "Select File Explorer" -msgstr "" +msgstr "Selecione Explorador de Arquivos" msgctxt "#30021" msgid "File Explorer" -msgstr "" +msgstr "Explorador de Arquivos" msgctxt "#30022" msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "" +msgstr "Ramo do Kodi atual (%s.%s-%s)" msgctxt "#30023" msgid "[B]Downloading:[/B]" -msgstr "" +msgstr "[B]Baixando:[/B]" msgctxt "#30024" msgid " | [B]Speed:[/B]" -msgstr "" +msgstr " | [B]Velocidade:[/B]" msgctxt "#30025" msgid " | [B]ETA:[/B]" diff --git a/script.kodi.android.update/resources/language/resource.language.ro_md/strings.po b/script.kodi.android.update/resources/language/resource.language.ro_md/strings.po deleted file mode 100644 index 30f7bd8cb..000000000 --- a/script.kodi.android.update/resources/language/resource.language.ro_md/strings.po +++ /dev/null @@ -1,129 +0,0 @@ -# Kodi Media Center language file -# Addon Name: Kodi Android Installer -# Addon id: script.kodi.android.update -# Addon Provider: Lunatixz -msgid "" -msgstr "" -"Project-Id-Version: script.kodi.android.update\n" -"Report-Msgid-Bugs-To: https://forum.kodi.tv/\n" -"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: ro_md\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n == 0 || n % 100 >= 2 && n % 100 <= 19) ? 1 : 2);\n" - -msgctxt "Addon Summary" -msgid "Download and Install Kodi packages." -msgstr "" - -msgctxt "Addon Description" -msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" - -msgctxt "#30000" -msgid "Enable Debugging [Log errors]" -msgstr "" - -msgctxt "#30001" -msgid "Something went wrong, Try again later..." -msgstr "" - -msgctxt "#30002" -msgid "Preparing to download: %s" -msgstr "" - -msgctxt "#30003" -msgid "Keep all downloads?" -msgstr "" - -msgctxt "#30004" -msgid "File already exists" -msgstr "" - -msgctxt "#30005" -msgid "Install" -msgstr "" - -msgctxt "#30006" -msgid "Redownload" -msgstr "" - -msgctxt "#30007" -msgid "Installation complete, file removed" -msgstr "" - -msgctxt "#30008" -msgid "Cleanup download after installation" -msgstr "" - -msgctxt "#30009" -msgid "Plugin disabled" -msgstr "" - -msgctxt "#30010" -msgid "Unknown Android version" -msgstr "" - -msgctxt "#30011" -msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" - -msgctxt "#30012" -msgid "Would you like to disable this plugin?" -msgstr "" - -msgctxt "#30013" -msgid "Detecting Android version..." -msgstr "" - -msgctxt "#30014" -msgid "Current Android platform (%s)" -msgstr "" - -msgctxt "#30015" -msgid "Semi-stable (Monthly builds)." -msgstr "" - -msgctxt "#30016" -msgid "Stable (Final releases)." -msgstr "" - -msgctxt "#30017" -msgid "Unstable (Daily builds)." -msgstr "" - -msgctxt "#30018" -msgid "Experimental (Debugging builds)." -msgstr "" - -msgctxt "#30019" -msgid "Incompatible platform" -msgstr "" - -msgctxt "#30020" -msgid "Select File Explorer" -msgstr "" - -msgctxt "#30021" -msgid "File Explorer" -msgstr "" - -msgctxt "#30022" -msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "" - -msgctxt "#30023" -msgid "[B]Downloading:[/B]" -msgstr "" - -msgctxt "#30024" -msgid " | [B]Speed:[/B]" -msgstr "" - -msgctxt "#30025" -msgid " | [B]ETA:[/B]" -msgstr "" diff --git a/script.kodi.android.update/resources/language/resource.language.scn/strings.po b/script.kodi.android.update/resources/language/resource.language.scn/strings.po deleted file mode 100644 index 4a5bc728e..000000000 --- a/script.kodi.android.update/resources/language/resource.language.scn/strings.po +++ /dev/null @@ -1,129 +0,0 @@ -# Kodi Media Center language file -# Addon Name: Kodi Android Installer -# Addon id: script.kodi.android.update -# Addon Provider: Lunatixz -msgid "" -msgstr "" -"Project-Id-Version: script.kodi.android.update\n" -"Report-Msgid-Bugs-To: https://forum.kodi.tv/\n" -"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: scn\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" - -msgctxt "Addon Summary" -msgid "Download and Install Kodi packages." -msgstr "" - -msgctxt "Addon Description" -msgid "Download and Install Kodi android packages; select from Nightlies, Releases, Snapshots and Test-builds." -msgstr "" - -msgctxt "#30000" -msgid "Enable Debugging [Log errors]" -msgstr "" - -msgctxt "#30001" -msgid "Something went wrong, Try again later..." -msgstr "" - -msgctxt "#30002" -msgid "Preparing to download: %s" -msgstr "" - -msgctxt "#30003" -msgid "Keep all downloads?" -msgstr "" - -msgctxt "#30004" -msgid "File already exists" -msgstr "" - -msgctxt "#30005" -msgid "Install" -msgstr "" - -msgctxt "#30006" -msgid "Redownload" -msgstr "" - -msgctxt "#30007" -msgid "Installation complete, file removed" -msgstr "" - -msgctxt "#30008" -msgid "Cleanup download after installation" -msgstr "" - -msgctxt "#30009" -msgid "Plugin disabled" -msgstr "" - -msgctxt "#30010" -msgid "Unknown Android version" -msgstr "" - -msgctxt "#30011" -msgid "Android v.%s.0.0 is incompatible with new Kodi versions," -msgstr "" - -msgctxt "#30012" -msgid "Would you like to disable this plugin?" -msgstr "" - -msgctxt "#30013" -msgid "Detecting Android version..." -msgstr "" - -msgctxt "#30014" -msgid "Current Android platform (%s)" -msgstr "" - -msgctxt "#30015" -msgid "Semi-stable (Monthly builds)." -msgstr "" - -msgctxt "#30016" -msgid "Stable (Final releases)." -msgstr "" - -msgctxt "#30017" -msgid "Unstable (Daily builds)." -msgstr "" - -msgctxt "#30018" -msgid "Experimental (Debugging builds)." -msgstr "" - -msgctxt "#30019" -msgid "Incompatible platform" -msgstr "" - -msgctxt "#30020" -msgid "Select File Explorer" -msgstr "" - -msgctxt "#30021" -msgid "File Explorer" -msgstr "" - -msgctxt "#30022" -msgid "Current Kodi Branch (%s.%s-%s)" -msgstr "" - -msgctxt "#30023" -msgid "[B]Downloading:[/B]" -msgstr "" - -msgctxt "#30024" -msgid " | [B]Speed:[/B]" -msgstr "" - -msgctxt "#30025" -msgid " | [B]ETA:[/B]" -msgstr "" diff --git a/script.kodi.android.update/select.py b/script.kodi.android.update/select.py index 5cd612c85..012ebcdae 100644 --- a/script.kodi.android.update/select.py +++ b/script.kodi.android.update/select.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023 Team-Kodi +# Copyright (C) 2024 Team-Kodi # # This file is part of script.kodi.android.update # diff --git a/script.kodi.android.update/service.py b/script.kodi.android.update/service.py index 9aaa3ae45..707599381 100644 --- a/script.kodi.android.update/service.py +++ b/script.kodi.android.update/service.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023 Team-Kodi +# Copyright (C) 2024 Team-Kodi # # This file is part of script.kodi.android.update # diff --git a/script.lockandenergycontrol/addon.py b/script.lockandenergycontrol/addon.py index 4c91f5dbc..e685fd870 100644 --- a/script.lockandenergycontrol/addon.py +++ b/script.lockandenergycontrol/addon.py @@ -6,13 +6,15 @@ class Monitor(xbmc.Monitor): - _powermanagement_displaysoff = 0 - _disabled_powermanagement_displaysoff = False - _windows_unlock = False def __init__(self) -> None: super().__init__() + self._powermanagement_displaysoff = 0 + self._disabled_powermanagement_displaysoff = False + self._windows_unlock = False + self._disable_displayoff_on_audio = False + self._player = xbmc.Player() self._update() def _update(self) -> None: @@ -22,6 +24,7 @@ def _update(self) -> None: self._windows_unlock = addon.getSettingBool("windows_unlock") self._powermanagement_displaysoff = addon.getSettingInt( "powermanagement_displaysoff") + self._disable_displayoff_on_audio = addon.getSettingBool("audio_displaysoff") self.reset_powermanagement_displaysoff() def onSettingsChanged(self) -> None: @@ -57,14 +60,23 @@ def set_windows_unlock(self, value: bool) -> bool: def _prevent_powermanagement_displaysoff(self) -> None: - is_fullscreen = xbmc.getCondVisibility("System.IsFullscreen") - if is_fullscreen and self._disabled_powermanagement_displaysoff: + fullscreen = xbmc.getCondVisibility("System.IsFullscreen") + audio = self._player.isPlayingAudio() + + if self._disabled_powermanagement_displaysoff and ((fullscreen and not audio) \ + or (not self._powermanagement_displaysoff and (not self._disable_displayoff_on_audio or not audio)) \ + or (not self._powermanagement_displaysoff and not fullscreen and self._disable_displayoff_on_audio and not audio) \ + or (not self._disable_displayoff_on_audio and fullscreen)): self.reset_powermanagement_displaysoff() - elif not is_fullscreen and not self._disabled_powermanagement_displaysoff: + elif not self._disabled_powermanagement_displaysoff and \ + ((self._powermanagement_displaysoff and not fullscreen) \ + or (self._disable_displayoff_on_audio and audio)): self._disabled_powermanagement_displaysoff = True self.set_powermanagement_displaysoff(0) + + def reset_powermanagement_displaysoff(self) -> None: if self._powermanagement_displaysoff: diff --git a/script.lockandenergycontrol/addon.xml b/script.lockandenergycontrol/addon.xml index 4a45cbe35..447578b2b 100644 --- a/script.lockandenergycontrol/addon.xml +++ b/script.lockandenergycontrol/addon.xml @@ -1,5 +1,5 @@ - + @@ -21,6 +21,9 @@ https://github.com/Heckie75/kodi-addon-prevent-sleep-and-lock https://github.com/Heckie75/kodi-addon-prevent-sleep-and-lock +v1.1.0 (2023-11-30) +- New feature to prevent display off when audio is playing + v1.0.0 (2022-06-10) - Initial version diff --git a/script.lockandenergycontrol/resources/language/resource.language.de_de/strings.po b/script.lockandenergycontrol/resources/language/resource.language.de_de/strings.po index f992c1d9b..966fbc196 100644 --- a/script.lockandenergycontrol/resources/language/resource.language.de_de/strings.po +++ b/script.lockandenergycontrol/resources/language/resource.language.de_de/strings.po @@ -17,10 +17,18 @@ msgctxt "#32030" msgid "General" msgstr "Allgemein" +msgctxt "#32031" +msgid "Don't put display to sleep on audio playback" +msgstr "Ruhezustand des Bildschirms bei Audiowiedergabe vermeiden" + msgctxt "#32032" msgid "Prevent lock screen" msgstr "Verhindere Bildschirmsperre" +msgctxt "#32033" +msgid "Deactivate sleep display on audio playback" +msgstr "Deaktiviere Ruhezustand des Bildschirms während der Audiowiedergabe" + msgctxt "#32130" msgid "Put display only in fullscreen mode to sleep" msgstr "Ruhezustand des Bildschirms bei Vollbild" diff --git a/script.lockandenergycontrol/resources/language/resource.language.en_gb/strings.po b/script.lockandenergycontrol/resources/language/resource.language.en_gb/strings.po index 59ebdfaea..59ebcc50e 100644 --- a/script.lockandenergycontrol/resources/language/resource.language.en_gb/strings.po +++ b/script.lockandenergycontrol/resources/language/resource.language.en_gb/strings.po @@ -17,10 +17,18 @@ msgctxt "#32030" msgid "General" msgstr "" +msgctxt "#32031" +msgid "Don't put display to sleep on audio playback" +msgstr "" + msgctxt "#32032" msgid "Prevent lock screen" msgstr "" +msgctxt "#32033" +msgid "Deactivate sleep display on audio playback" +msgstr "" + msgctxt "#32130" msgid "Put display only in fullscreen mode to sleep" msgstr "" diff --git a/script.lockandenergycontrol/resources/settings.xml b/script.lockandenergycontrol/resources/settings.xml index 66434cb57..dacf81ad4 100644 --- a/script.lockandenergycontrol/resources/settings.xml +++ b/script.lockandenergycontrol/resources/settings.xml @@ -68,6 +68,11 @@ false
+ + 3 + false + + 0 false @@ -79,4 +84,4 @@
- \ No newline at end of file + diff --git a/script.metadata.editor/addon.xml b/script.metadata.editor/addon.xml index 214073ca8..62f571883 100644 --- a/script.metadata.editor/addon.xml +++ b/script.metadata.editor/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/script.metadata.editor/resources/language/resource.language.de_DE/strings.po b/script.metadata.editor/resources/language/resource.language.de_DE/strings.po index c0ad55dcd..13da79841 100644 --- a/script.metadata.editor/resources/language/resource.language.de_DE/strings.po +++ b/script.metadata.editor/resources/language/resource.language.de_DE/strings.po @@ -125,7 +125,7 @@ msgstr "Als Standard setzen?" #: /resources/settings.xml msgctxt "#32021" msgid "Enable .nfo updating for movies, TV shows and music videos" -msgstr "Aktiviere .nfo Updating für Filme, Serien und Musikvideos" +msgstr "Aktiviere .nfo Aktualisierung für Filme, Serien und Musikvideos" #: /resources/lib/editor.py msgctxt "#32022" @@ -155,7 +155,7 @@ msgstr "Hohe Serverlast. Bitte warten." #: /resources/settings.xml msgctxt "#32027" msgid "TV show library is based on informations of" -msgstr "Serien-Datenbank ist basierend auf Informationen von" +msgstr "Serien-Datenbank basiert auf Informationen von" #: /resources/settings.xml msgctxt "#32029" @@ -170,7 +170,7 @@ msgstr "Bewertungs-Updater" #: /resources/settings.xml msgctxt "#32028" msgid "Run updating process in the background" -msgstr "Führe das Updaten im Hintergrund aus" +msgstr "Führe die Aktualisierung im Hintergrund aus" #: /resources/settings.xml msgctxt "#32031" @@ -204,7 +204,7 @@ msgstr "" #: /default.py msgctxt "#32036" msgid "Update TV show ratings" -msgstr "Aktutalisiere Serienbewertungen" +msgstr "Aktualisiere Serienbewertungen" #: /resources/lib/rating_updater.py msgctxt "#32033" @@ -223,7 +223,7 @@ msgstr "Erstelle .nfo Datei, falls diese fehlt" #: /default.py msgctxt "#32037" msgid "Update movie ratings" -msgstr "Aktutalisiere Filmbewertungen" +msgstr "Aktualisiere Filmbewertungen" #: /default.py msgctxt "#32038" @@ -300,7 +300,7 @@ msgctxt "#32052" msgid "Fallback to US MPAA if configured country has no certification stored" msgstr "" "Nutze US MPAA als Fallback, falls keine Zertifizierung der konfigurierten " -"Region gefunden wurde" +"Region gefunden wird" #: /resources/settings.xml msgctxt "#32053" diff --git a/script.metadata.editor/resources/lib/helper.py b/script.metadata.editor/resources/lib/helper.py index 99e8ee1b1..45694dcbb 100644 --- a/script.metadata.editor/resources/lib/helper.py +++ b/script.metadata.editor/resources/lib/helper.py @@ -24,7 +24,7 @@ ADDON = xbmcaddon.Addon() ADDON_ID = ADDON.getAddonInfo('id') -ADDON_DATA_PATH = os.path.join(xbmc.translatePath("special://profile/addon_data/%s" % ADDON_ID)) +ADDON_DATA_PATH = os.path.join(xbmcvfs.translatePath("special://profile/addon_data/%s" % ADDON_ID)) NOTICE = xbmc.LOGINFO WARNING = xbmc.LOGWARNING diff --git a/script.metadata.editor/resources/lib/nfo_updater.py b/script.metadata.editor/resources/lib/nfo_updater.py index c93428814..66e6c9dbc 100644 --- a/script.metadata.editor/resources/lib/nfo_updater.py +++ b/script.metadata.editor/resources/lib/nfo_updater.py @@ -108,7 +108,7 @@ def write_file(self): xml_prettyprint(self.root) - content = ET.tostring(self.root, encoding='UTF-8', method='xml', xml_declaration=True).decode() + content = ET.tostring(self.root, encoding='UTF8', method='xml').decode() with xbmcvfs.File(self.targetfile, 'w') as f: result = f.write(content) diff --git a/script.module.arrow/LICENSE.txt b/script.module.arrow/LICENSE.txt index 87452e660..4f9eea5d1 100644 --- a/script.module.arrow/LICENSE.txt +++ b/script.module.arrow/LICENSE.txt @@ -1,4 +1,192 @@ -Copyright 2013 Chris Smith + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Chris Smith Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10,4 +198,4 @@ Copyright 2013 Chris Smith distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/script.module.arrow/README.md b/script.module.arrow/README.md deleted file mode 100644 index 3c886e116..000000000 --- a/script.module.arrow/README.md +++ /dev/null @@ -1,6 +0,0 @@ -script.module.arrow -====================== - -Python arrow library packed for Kodi. - -See https://github.com/crsmithdev/arrow diff --git a/script.module.arrow/addon.xml b/script.module.arrow/addon.xml index 7befdadeb..1971bec8c 100644 --- a/script.module.arrow/addon.xml +++ b/script.module.arrow/addon.xml @@ -1,24 +1,20 @@ - + - - - + + + - + - Arrow: better dates and times for Python - Packed for Kodi from https://github.com/crsmithdev/arrow + Better dates and times for Python + Better dates and times for Python + Apache-2.0 all - Apache2 - https://github.com/Razzeee/script.module.arrow - beenje AT gmail.com + https://arrow.readthedocs.io/en/latest/ + https://github.com/arrow-py/arrow - icon.png + resources/icon.png diff --git a/script.module.arrow/lib/CHANGELOG.rst b/script.module.arrow/lib/CHANGELOG.rst deleted file mode 100644 index 300af2089..000000000 --- a/script.module.arrow/lib/CHANGELOG.rst +++ /dev/null @@ -1,647 +0,0 @@ -Changelog -========= - -1.0.3 (2021-03-05) ------------------- - -- [FIX] Updated internals to avoid issues when running ``mypy --strict``. -- [FIX] Corrections to Swedish locale. -- [INTERNAL] Lowered required coverage limit until ``humanize`` month tests are fixed. - -1.0.2 (2021-02-28) ------------------- - -- [FIXED] Fixed an ``OverflowError`` that could occur when running Arrow on a 32-bit OS. - -1.0.1 (2021-02-27) ------------------- - -- [FIXED] A ``py.typed`` file is now bundled with the Arrow package to conform to PEP 561. - -1.0.0 (2021-02-26) ------------------- - -After 8 years we're pleased to announce Arrow v1.0. Thanks to the entire Python community for helping make Arrow the amazing package it is today! - -- [CHANGE] Arrow has **dropped support** for Python 2.7 and 3.5. -- [CHANGE] There are multiple **breaking changes** with this release, please see the `migration guide `_ for a complete overview. -- [CHANGE] Arrow is now following `semantic versioning `_. -- [CHANGE] Made ``humanize`` granularity="auto" limits more accurate to reduce strange results. -- [NEW] Added support for Python 3.9. -- [NEW] Added a new keyword argument "exact" to ``span``, ``span_range`` and ``interval`` methods. This makes timespans begin at the start time given and not extend beyond the end time given, for example: - -.. code-block:: python - - >>> start = Arrow(2021, 2, 5, 12, 30) - >>> end = Arrow(2021, 2, 5, 17, 15) - >>> for r in arrow.Arrow.span_range('hour', start, end, exact=True): - ... print(r) - ... - (, ) - (, ) - (, ) - (, ) - (, ) - -- [NEW] Arrow now natively supports PEP 484-style type annotations. -- [FIX] Fixed handling of maximum permitted timestamp on Windows systems. -- [FIX] Corrections to French, German, Japanese and Norwegian locales. -- [INTERNAL] Raise more appropriate errors when string parsing fails to match. - -0.17.0 (2020-10-2) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. This is the last major release to support Python 2.7 and Python 3.5. -- [NEW] Arrow now properly handles imaginary datetimes during DST shifts. For example: - -.. code-block:: python - - >>> just_before = arrow.get(2013, 3, 31, 1, 55, tzinfo="Europe/Paris") - >>> just_before.shift(minutes=+10) - - -.. code-block:: python - - >>> before = arrow.get("2018-03-10 23:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific") - >>> after = arrow.get("2018-03-11 04:00:00", "YYYY-MM-DD HH:mm:ss", tzinfo="US/Pacific") - >>> result=[(t, t.to("utc")) for t in arrow.Arrow.range("hour", before, after)] - >>> for r in result: - ... print(r) - ... - (, ) - (, ) - (, ) - (, ) - (, ) - -- [NEW] Added ``humanize`` week granularity translation for Tagalog. -- [CHANGE] Calls to the ``timestamp`` property now emit a ``DeprecationWarning``. In a future release, ``timestamp`` will be changed to a method to align with Python's datetime module. If you would like to continue using the property, please change your code to use the ``int_timestamp`` or ``float_timestamp`` properties instead. -- [CHANGE] Expanded and improved Catalan locale. -- [FIX] Fixed a bug that caused ``Arrow.range()`` to incorrectly cut off ranges in certain scenarios when using month, quarter, or year endings. -- [FIX] Fixed a bug that caused day of week token parsing to be case sensitive. -- [INTERNAL] A number of functions were reordered in arrow.py for better organization and grouping of related methods. This change will have no impact on usage. -- [INTERNAL] A minimum tox version is now enforced for compatibility reasons. Contributors must use tox >3.18.0 going forward. - -0.16.0 (2020-08-23) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.16.x and 0.17.x releases are the last to support Python 2.7 and 3.5. -- [NEW] Implemented `PEP 495 `_ to handle ambiguous datetimes. This is achieved by the addition of the ``fold`` attribute for Arrow objects. For example: - -.. code-block:: python - - >>> before = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm') - - >>> before.fold - 0 - >>> before.ambiguous - True - >>> after = Arrow(2017, 10, 29, 2, 0, tzinfo='Europe/Stockholm', fold=1) - - >>> after = before.replace(fold=1) - - -- [NEW] Added ``normalize_whitespace`` flag to ``arrow.get``. This is useful for parsing log files and/or any files that may contain inconsistent spacing. For example: - -.. code-block:: python - - >>> arrow.get("Jun 1 2005 1:33PM", "MMM D YYYY H:mmA", normalize_whitespace=True) - - >>> arrow.get("2013-036 \t 04:05:06Z", normalize_whitespace=True) - - -0.15.8 (2020-07-23) -------------------- - -- [WARN] Arrow will **drop support** for Python 2.7 and 3.5 in the upcoming 1.0.0 release. The 0.15.x, 0.16.x, and 0.17.x releases are the last to support Python 2.7 and 3.5. -- [NEW] Added ``humanize`` week granularity translation for Czech. -- [FIX] ``arrow.get`` will now pick sane defaults when weekdays are passed with particular token combinations, see `#446 `_. -- [INTERNAL] Moved arrow to an organization. The repo can now be found `here `_. -- [INTERNAL] Started issuing deprecation warnings for Python 2.7 and 3.5. -- [INTERNAL] Added Python 3.9 to CI pipeline. - -0.15.7 (2020-06-19) -------------------- - -- [NEW] Added a number of built-in format strings. See the `docs `_ for a complete list of supported formats. For example: - -.. code-block:: python - - >>> arw = arrow.utcnow() - >>> arw.format(arrow.FORMAT_COOKIE) - 'Wednesday, 27-May-2020 10:30:35 UTC' - -- [NEW] Arrow is now fully compatible with Python 3.9 and PyPy3. -- [NEW] Added Makefile, tox.ini, and requirements.txt files to the distribution bundle. -- [NEW] Added French Canadian and Swahili locales. -- [NEW] Added ``humanize`` week granularity translation for Hebrew, Greek, Macedonian, Swedish, Slovak. -- [FIX] ms and μs timestamps are now normalized in ``arrow.get()``, ``arrow.fromtimestamp()``, and ``arrow.utcfromtimestamp()``. For example: - -.. code-block:: python - - >>> ts = 1591161115194556 - >>> arw = arrow.get(ts) - - >>> arw.timestamp - 1591161115 - -- [FIX] Refactored and updated Macedonian, Hebrew, Korean, and Portuguese locales. - -0.15.6 (2020-04-29) -------------------- - -- [NEW] Added support for parsing and formatting `ISO 8601 week dates `_ via a new token ``W``, for example: - -.. code-block:: python - - >>> arrow.get("2013-W29-6", "W") - - >>> utc=arrow.utcnow() - >>> utc - - >>> utc.format("W") - '2020-W04-4' - -- [NEW] Formatting with ``x`` token (microseconds) is now possible, for example: - -.. code-block:: python - - >>> dt = arrow.utcnow() - >>> dt.format("x") - '1585669870688329' - >>> dt.format("X") - '1585669870' - -- [NEW] Added ``humanize`` week granularity translation for German, Italian, Polish & Taiwanese locales. -- [FIX] Consolidated and simplified German locales. -- [INTERNAL] Moved testing suite from nosetest/Chai to pytest/pytest-mock. -- [INTERNAL] Converted xunit-style setup and teardown functions in tests to pytest fixtures. -- [INTERNAL] Setup Github Actions for CI alongside Travis. -- [INTERNAL] Help support Arrow's future development by donating to the project on `Open Collective `_. - -0.15.5 (2020-01-03) -------------------- - -- [WARN] Python 2 reached EOL on 2020-01-01. arrow will **drop support** for Python 2 in a future release to be decided (see `#739 `_). -- [NEW] Added bounds parameter to ``span_range``, ``interval`` and ``span`` methods. This allows you to include or exclude the start and end values. -- [NEW] ``arrow.get()`` can now create arrow objects from a timestamp with a timezone, for example: - -.. code-block:: python - - >>> arrow.get(1367900664, tzinfo=tz.gettz('US/Pacific')) - - -- [NEW] ``humanize`` can now combine multiple levels of granularity, for example: - -.. code-block:: python - - >>> later140 = arrow.utcnow().shift(seconds=+8400) - >>> later140.humanize(granularity="minute") - 'in 139 minutes' - >>> later140.humanize(granularity=["hour", "minute"]) - 'in 2 hours and 19 minutes' - -- [NEW] Added Hong Kong locale (``zh_hk``). -- [NEW] Added ``humanize`` week granularity translation for Dutch. -- [NEW] Numbers are now displayed when using the seconds granularity in ``humanize``. -- [CHANGE] ``range`` now supports both the singular and plural forms of the ``frames`` argument (e.g. day and days). -- [FIX] Improved parsing of strings that contain punctuation. -- [FIX] Improved behaviour of ``humanize`` when singular seconds are involved. - -0.15.4 (2019-11-02) -------------------- - -- [FIX] Fixed an issue that caused package installs to fail on Conda Forge. - -0.15.3 (2019-11-02) -------------------- - -- [NEW] ``factory.get()`` can now create arrow objects from a ISO calendar tuple, for example: - -.. code-block:: python - - >>> arrow.get((2013, 18, 7)) - - -- [NEW] Added a new token ``x`` to allow parsing of integer timestamps with milliseconds and microseconds. -- [NEW] Formatting now supports escaping of characters using the same syntax as parsing, for example: - -.. code-block:: python - - >>> arw = arrow.now() - >>> fmt = "YYYY-MM-DD h [h] m" - >>> arw.format(fmt) - '2019-11-02 3 h 32' - -- [NEW] Added ``humanize`` week granularity translations for Chinese, Spanish and Vietnamese. -- [CHANGE] Added ``ParserError`` to module exports. -- [FIX] Added support for midnight at end of day. See `#703 `_ for details. -- [INTERNAL] Created Travis build for macOS. -- [INTERNAL] Test parsing and formatting against full timezone database. - -0.15.2 (2019-09-14) -------------------- - -- [NEW] Added ``humanize`` week granularity translations for Portuguese and Brazilian Portuguese. -- [NEW] Embedded changelog within docs and added release dates to versions. -- [FIX] Fixed a bug that caused test failures on Windows only, see `#668 `_ for details. - -0.15.1 (2019-09-10) -------------------- - -- [NEW] Added ``humanize`` week granularity translations for Japanese. -- [FIX] Fixed a bug that caused Arrow to fail when passed a negative timestamp string. -- [FIX] Fixed a bug that caused Arrow to fail when passed a datetime object with ``tzinfo`` of type ``StaticTzInfo``. - -0.15.0 (2019-09-08) -------------------- - -- [NEW] Added support for DDD and DDDD ordinal date tokens. The following functionality is now possible: ``arrow.get("1998-045")``, ``arrow.get("1998-45", "YYYY-DDD")``, ``arrow.get("1998-045", "YYYY-DDDD")``. -- [NEW] ISO 8601 basic format for dates and times is now supported (e.g. ``YYYYMMDDTHHmmssZ``). -- [NEW] Added ``humanize`` week granularity translations for French, Russian and Swiss German locales. -- [CHANGE] Timestamps of type ``str`` are no longer supported **without a format string** in the ``arrow.get()`` method. This change was made to support the ISO 8601 basic format and to address bugs such as `#447 `_. - -The following will NOT work in v0.15.0: - -.. code-block:: python - - >>> arrow.get("1565358758") - >>> arrow.get("1565358758.123413") - -The following will work in v0.15.0: - -.. code-block:: python - - >>> arrow.get("1565358758", "X") - >>> arrow.get("1565358758.123413", "X") - >>> arrow.get(1565358758) - >>> arrow.get(1565358758.123413) - -- [CHANGE] When a meridian token (a|A) is passed and no meridians are available for the specified locale (e.g. unsupported or untranslated) a ``ParserError`` is raised. -- [CHANGE] The timestamp token (``X``) will now match float timestamps of type ``str``: ``arrow.get(“1565358758.123415”, “X”)``. -- [CHANGE] Strings with leading and/or trailing whitespace will no longer be parsed without a format string. Please see `the docs `_ for ways to handle this. -- [FIX] The timestamp token (``X``) will now only match on strings that **strictly contain integers and floats**, preventing incorrect matches. -- [FIX] Most instances of ``arrow.get()`` returning an incorrect ``Arrow`` object from a partial parsing match have been eliminated. The following issue have been addressed: `#91 `_, `#196 `_, `#396 `_, `#434 `_, `#447 `_, `#456 `_, `#519 `_, `#538 `_, `#560 `_. - -0.14.7 (2019-09-04) -------------------- - -- [CHANGE] ``ArrowParseWarning`` will no longer be printed on every call to ``arrow.get()`` with a datetime string. The purpose of the warning was to start a conversation about the upcoming 0.15.0 changes and we appreciate all the feedback that the community has given us! - -0.14.6 (2019-08-28) -------------------- - -- [NEW] Added support for ``week`` granularity in ``Arrow.humanize()``. For example, ``arrow.utcnow().shift(weeks=-1).humanize(granularity="week")`` outputs "a week ago". This change introduced two new untranslated words, ``week`` and ``weeks``, to all locale dictionaries, so locale contributions are welcome! -- [NEW] Fully translated the Brazilian Portugese locale. -- [CHANGE] Updated the Macedonian locale to inherit from a Slavic base. -- [FIX] Fixed a bug that caused ``arrow.get()`` to ignore tzinfo arguments of type string (e.g. ``arrow.get(tzinfo="Europe/Paris")``). -- [FIX] Fixed a bug that occurred when ``arrow.Arrow()`` was instantiated with a ``pytz`` tzinfo object. -- [FIX] Fixed a bug that caused Arrow to fail when passed a sub-second token, that when rounded, had a value greater than 999999 (e.g. ``arrow.get("2015-01-12T01:13:15.9999995")``). Arrow should now accurately propagate the rounding for large sub-second tokens. - -0.14.5 (2019-08-09) -------------------- - -- [NEW] Added Afrikaans locale. -- [CHANGE] Removed deprecated ``replace`` shift functionality. Users looking to pass plural properties to the ``replace`` function to shift values should use ``shift`` instead. -- [FIX] Fixed bug that occurred when ``factory.get()`` was passed a locale kwarg. - -0.14.4 (2019-07-30) -------------------- - -- [FIX] Fixed a regression in 0.14.3 that prevented a tzinfo argument of type string to be passed to the ``get()`` function. Functionality such as ``arrow.get("2019072807", "YYYYMMDDHH", tzinfo="UTC")`` should work as normal again. -- [CHANGE] Moved ``backports.functools_lru_cache`` dependency from ``extra_requires`` to ``install_requires`` for ``Python 2.7`` installs to fix `#495 `_. - -0.14.3 (2019-07-28) -------------------- - -- [NEW] Added full support for Python 3.8. -- [CHANGE] Added warnings for upcoming factory.get() parsing changes in 0.15.0. Please see `#612 `_ for full details. -- [FIX] Extensive refactor and update of documentation. -- [FIX] factory.get() can now construct from kwargs. -- [FIX] Added meridians to Spanish Locale. - -0.14.2 (2019-06-06) -------------------- - -- [CHANGE] Travis CI builds now use tox to lint and run tests. -- [FIX] Fixed UnicodeDecodeError on certain locales (#600). - -0.14.1 (2019-06-06) -------------------- - -- [FIX] Fixed ``ImportError: No module named 'dateutil'`` (#598). - -0.14.0 (2019-06-06) -------------------- - -- [NEW] Added provisional support for Python 3.8. -- [CHANGE] Removed support for EOL Python 3.4. -- [FIX] Updated setup.py with modern Python standards. -- [FIX] Upgraded dependencies to latest versions. -- [FIX] Enabled flake8 and black on travis builds. -- [FIX] Formatted code using black and isort. - -0.13.2 (2019-05-30) -------------------- - -- [NEW] Add is_between method. -- [FIX] Improved humanize behaviour for near zero durations (#416). -- [FIX] Correct humanize behaviour with future days (#541). -- [FIX] Documentation updates. -- [FIX] Improvements to German Locale. - -0.13.1 (2019-02-17) -------------------- - -- [NEW] Add support for Python 3.7. -- [CHANGE] Remove deprecation decorators for Arrow.range(), Arrow.span_range() and Arrow.interval(), all now return generators, wrap with list() to get old behavior. -- [FIX] Documentation and docstring updates. - -0.13.0 (2019-01-09) -------------------- - -- [NEW] Added support for Python 3.6. -- [CHANGE] Drop support for Python 2.6/3.3. -- [CHANGE] Return generator instead of list for Arrow.range(), Arrow.span_range() and Arrow.interval(). -- [FIX] Make arrow.get() work with str & tzinfo combo. -- [FIX] Make sure special RegEx characters are escaped in format string. -- [NEW] Added support for ZZZ when formatting. -- [FIX] Stop using datetime.utcnow() in internals, use datetime.now(UTC) instead. -- [FIX] Return NotImplemented instead of TypeError in arrow math internals. -- [NEW] Added Estonian Locale. -- [FIX] Small fixes to Greek locale. -- [FIX] TagalogLocale improvements. -- [FIX] Added test requirements to setup. -- [FIX] Improve docs for get, now and utcnow methods. -- [FIX] Correct typo in depreciation warning. - -0.12.1 ------- - -- [FIX] Allow universal wheels to be generated and reliably installed. -- [FIX] Make humanize respect only_distance when granularity argument is also given. - -0.12.0 ------- - -- [FIX] Compatibility fix for Python 2.x - -0.11.0 ------- - -- [FIX] Fix grammar of ArabicLocale -- [NEW] Add Nepali Locale -- [FIX] Fix month name + rename AustriaLocale -> AustrianLocale -- [FIX] Fix typo in Basque Locale -- [FIX] Fix grammar in PortugueseBrazilian locale -- [FIX] Remove pip --user-mirrors flag -- [NEW] Add Indonesian Locale - -0.10.0 ------- - -- [FIX] Fix getattr off by one for quarter -- [FIX] Fix negative offset for UTC -- [FIX] Update arrow.py - -0.9.0 ------ - -- [NEW] Remove duplicate code -- [NEW] Support gnu date iso 8601 -- [NEW] Add support for universal wheels -- [NEW] Slovenian locale -- [NEW] Slovak locale -- [NEW] Romanian locale -- [FIX] respect limit even if end is defined range -- [FIX] Separate replace & shift functions -- [NEW] Added tox -- [FIX] Fix supported Python versions in documentation -- [NEW] Azerbaijani locale added, locale issue fixed in Turkish. -- [FIX] Format ParserError's raise message - -0.8.0 ------ - -- [] - -0.7.1 ------ - -- [NEW] Esperanto locale (batisteo) - -0.7.0 ------ - -- [FIX] Parse localized strings #228 (swistakm) -- [FIX] Modify tzinfo parameter in ``get`` api #221 (bottleimp) -- [FIX] Fix Czech locale (PrehistoricTeam) -- [FIX] Raise TypeError when adding/subtracting non-dates (itsmeolivia) -- [FIX] Fix pytz conversion error (Kudo) -- [FIX] Fix overzealous time truncation in span_range (kdeldycke) -- [NEW] Humanize for time duration #232 (ybrs) -- [NEW] Add Thai locale (sipp11) -- [NEW] Adding Belarusian (be) locale (oire) -- [NEW] Search date in strings (beenje) -- [NEW] Note that arrow's tokens differ from strptime's. (offby1) - -0.6.0 ------ - -- [FIX] Added support for Python 3 -- [FIX] Avoid truncating oversized epoch timestamps. Fixes #216. -- [FIX] Fixed month abbreviations for Ukrainian -- [FIX] Fix typo timezone -- [FIX] A couple of dialect fixes and two new languages -- [FIX] Spanish locale: ``Miercoles`` should have acute accent -- [Fix] Fix Finnish grammar -- [FIX] Fix typo in 'Arrow.floor' docstring -- [FIX] Use read() utility to open README -- [FIX] span_range for week frame -- [NEW] Add minimal support for fractional seconds longer than six digits. -- [NEW] Adding locale support for Marathi (mr) -- [NEW] Add count argument to span method -- [NEW] Improved docs - -0.5.1 - 0.5.4 -------------- - -- [FIX] test the behavior of simplejson instead of calling for_json directly (tonyseek) -- [FIX] Add Hebrew Locale (doodyparizada) -- [FIX] Update documentation location (andrewelkins) -- [FIX] Update setup.py Development Status level (andrewelkins) -- [FIX] Case insensitive month match (cshowe) - -0.5.0 ------ - -- [NEW] struct_time addition. (mhworth) -- [NEW] Version grep (eirnym) -- [NEW] Default to ISO 8601 format (emonty) -- [NEW] Raise TypeError on comparison (sniekamp) -- [NEW] Adding Macedonian(mk) locale (krisfremen) -- [FIX] Fix for ISO seconds and fractional seconds (sdispater) (andrewelkins) -- [FIX] Use correct Dutch wording for "hours" (wbolster) -- [FIX] Complete the list of english locales (indorilftw) -- [FIX] Change README to reStructuredText (nyuszika7h) -- [FIX] Parse lower-cased 'h' (tamentis) -- [FIX] Slight modifications to Dutch locale (nvie) - -0.4.4 ------ - -- [NEW] Include the docs in the released tarball -- [NEW] Czech localization Czech localization for Arrow -- [NEW] Add fa_ir to locales -- [FIX] Fixes parsing of time strings with a final Z -- [FIX] Fixes ISO parsing and formatting for fractional seconds -- [FIX] test_fromtimestamp sp -- [FIX] some typos fixed -- [FIX] removed an unused import statement -- [FIX] docs table fix -- [FIX] Issue with specify 'X' template and no template at all to arrow.get -- [FIX] Fix "import" typo in docs/index.rst -- [FIX] Fix unit tests for zero passed -- [FIX] Update layout.html -- [FIX] In Norwegian and new Norwegian months and weekdays should not be capitalized -- [FIX] Fixed discrepancy between specifying 'X' to arrow.get and specifying no template - -0.4.3 ------ - -- [NEW] Turkish locale (Emre) -- [NEW] Arabic locale (Mosab Ahmad) -- [NEW] Danish locale (Holmars) -- [NEW] Icelandic locale (Holmars) -- [NEW] Hindi locale (Atmb4u) -- [NEW] Malayalam locale (Atmb4u) -- [NEW] Finnish locale (Stormpat) -- [NEW] Portuguese locale (Danielcorreia) -- [NEW] ``h`` and ``hh`` strings are now supported (Averyonghub) -- [FIX] An incorrect inflection in the Polish locale has been fixed (Avalanchy) -- [FIX] ``arrow.get`` now properly handles ``Date`` (Jaapz) -- [FIX] Tests are now declared in ``setup.py`` and the manifest (Pypingou) -- [FIX] ``__version__`` has been added to ``__init__.py`` (Sametmax) -- [FIX] ISO 8601 strings can be parsed without a separator (Ivandiguisto / Root) -- [FIX] Documentation is now more clear regarding some inputs on ``arrow.get`` (Eriktaubeneck) -- [FIX] Some documentation links have been fixed (Vrutsky) -- [FIX] Error messages for parse errors are now more descriptive (Maciej Albin) -- [FIX] The parser now correctly checks for separators in strings (Mschwager) - -0.4.2 ------ - -- [NEW] Factory ``get`` method now accepts a single ``Arrow`` argument. -- [NEW] Tokens SSSS, SSSSS and SSSSSS are supported in parsing. -- [NEW] ``Arrow`` objects have a ``float_timestamp`` property. -- [NEW] Vietnamese locale (Iu1nguoi) -- [NEW] Factory ``get`` method now accepts a list of format strings (Dgilland) -- [NEW] A MANIFEST.in file has been added (Pypingou) -- [NEW] Tests can be run directly from ``setup.py`` (Pypingou) -- [FIX] Arrow docs now list 'day of week' format tokens correctly (Rudolphfroger) -- [FIX] Several issues with the Korean locale have been resolved (Yoloseem) -- [FIX] ``humanize`` now correctly returns unicode (Shvechikov) -- [FIX] ``Arrow`` objects now pickle / unpickle correctly (Yoloseem) - -0.4.1 ------ - -- [NEW] Table / explanation of formatting & parsing tokens in docs -- [NEW] Brazilian locale (Augusto2112) -- [NEW] Dutch locale (OrangeTux) -- [NEW] Italian locale (Pertux) -- [NEW] Austrain locale (LeChewbacca) -- [NEW] Tagalog locale (Marksteve) -- [FIX] Corrected spelling and day numbers in German locale (LeChewbacca) -- [FIX] Factory ``get`` method should now handle unicode strings correctly (Bwells) -- [FIX] Midnight and noon should now parse and format correctly (Bwells) - -0.4.0 ------ - -- [NEW] Format-free ISO 8601 parsing in factory ``get`` method -- [NEW] Support for 'week' / 'weeks' in ``span``, ``range``, ``span_range``, ``floor`` and ``ceil`` -- [NEW] Support for 'weeks' in ``replace`` -- [NEW] Norwegian locale (Martinp) -- [NEW] Japanese locale (CortYuming) -- [FIX] Timezones no longer show the wrong sign when formatted (Bean) -- [FIX] Microseconds are parsed correctly from strings (Bsidhom) -- [FIX] Locale day-of-week is no longer off by one (Cynddl) -- [FIX] Corrected plurals of Ukrainian and Russian nouns (Catchagain) -- [CHANGE] Old 0.1 ``arrow`` module method removed -- [CHANGE] Dropped timestamp support in ``range`` and ``span_range`` (never worked correctly) -- [CHANGE] Dropped parsing of single string as tz string in factory ``get`` method (replaced by ISO 8601) - -0.3.5 ------ - -- [NEW] French locale (Cynddl) -- [NEW] Spanish locale (Slapresta) -- [FIX] Ranges handle multiple timezones correctly (Ftobia) - -0.3.4 ------ - -- [FIX] Humanize no longer sometimes returns the wrong month delta -- [FIX] ``__format__`` works correctly with no format string - -0.3.3 ------ - -- [NEW] Python 2.6 support -- [NEW] Initial support for locale-based parsing and formatting -- [NEW] ArrowFactory class, now proxied as the module API -- [NEW] ``factory`` api method to obtain a factory for a custom type -- [FIX] Python 3 support and tests completely ironed out - -0.3.2 ------ - -- [NEW] Python 3+ support - -0.3.1 ------ - -- [FIX] The old ``arrow`` module function handles timestamps correctly as it used to - -0.3.0 ------ - -- [NEW] ``Arrow.replace`` method -- [NEW] Accept timestamps, datetimes and Arrows for datetime inputs, where reasonable -- [FIX] ``range`` and ``span_range`` respect end and limit parameters correctly -- [CHANGE] Arrow objects are no longer mutable -- [CHANGE] Plural attribute name semantics altered: single -> absolute, plural -> relative -- [CHANGE] Plural names no longer supported as properties (e.g. ``arrow.utcnow().years``) - -0.2.1 ------ - -- [NEW] Support for localized humanization -- [NEW] English, Russian, Greek, Korean, Chinese locales - -0.2.0 ------ - -- **REWRITE** -- [NEW] Date parsing -- [NEW] Date formatting -- [NEW] ``floor``, ``ceil`` and ``span`` methods -- [NEW] ``datetime`` interface implementation -- [NEW] ``clone`` method -- [NEW] ``get``, ``now`` and ``utcnow`` API methods - -0.1.6 ------ - -- [NEW] Humanized time deltas -- [NEW] ``__eq__`` implemented -- [FIX] Issues with conversions related to daylight savings time resolved -- [CHANGE] ``__str__`` uses ISO formatting - -0.1.5 ------ - -- **Started tracking changes** -- [NEW] Parsing of ISO-formatted time zone offsets (e.g. '+02:30', '-05:00') -- [NEW] Resolved some issues with timestamps and delta / Olson time zones diff --git a/script.module.arrow/lib/LICENSE b/script.module.arrow/lib/LICENSE deleted file mode 100644 index 4f9eea5d1..000000000 --- a/script.module.arrow/lib/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021 Chris Smith - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/script.module.arrow/lib/README.rst b/script.module.arrow/lib/README.rst deleted file mode 100644 index abdf75560..000000000 --- a/script.module.arrow/lib/README.rst +++ /dev/null @@ -1,134 +0,0 @@ -Arrow: Better dates & times for Python -====================================== - -.. start-inclusion-marker-do-not-remove - -.. image:: https://github.com/arrow-py/arrow/workflows/tests/badge.svg?branch=master - :alt: Build Status - :target: https://github.com/arrow-py/arrow/actions?query=workflow%3Atests+branch%3Amaster - -.. image:: https://codecov.io/gh/arrow-py/arrow/branch/master/graph/badge.svg - :alt: Coverage - :target: https://codecov.io/gh/arrow-py/arrow - -.. image:: https://img.shields.io/pypi/v/arrow.svg - :alt: PyPI Version - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/pypi/pyversions/arrow.svg - :alt: Supported Python Versions - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/pypi/l/arrow.svg - :alt: License - :target: https://pypi.python.org/pypi/arrow - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :alt: Code Style: Black - :target: https://github.com/psf/black - - -**Arrow** is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps. It implements and updates the datetime type, plugging gaps in functionality and providing an intelligent module API that supports many common creation scenarios. Simply put, it helps you work with dates and times with fewer imports and a lot less code. - -Arrow is named after the `arrow of time `_ and is heavily inspired by `moment.js `_ and `requests `_. - -Why use Arrow over built-in modules? ------------------------------------- - -Python's standard library and some other low-level modules have near-complete date, time and timezone functionality, but don't work very well from a usability perspective: - -- Too many modules: datetime, time, calendar, dateutil, pytz and more -- Too many types: date, time, datetime, tzinfo, timedelta, relativedelta, etc. -- Timezones and timestamp conversions are verbose and unpleasant -- Timezone naivety is the norm -- Gaps in functionality: ISO 8601 parsing, timespans, humanization - -Features --------- - -- Fully-implemented, drop-in replacement for datetime -- Support for Python 3.6+ -- Timezone-aware and UTC by default -- Super-simple creation options for many common input scenarios -- ``shift`` method with support for relative offsets, including weeks -- Format and parse strings automatically -- Wide support for the `ISO 8601 `_ standard -- Timezone conversion -- Support for ``dateutil``, ``pytz``, and ``ZoneInfo`` tzinfo objects -- Generates time spans, ranges, floors and ceilings for time frames ranging from microsecond to year -- Humanize dates and times with a growing list of contributed locales -- Extensible for your own Arrow-derived types -- Full support for PEP 484-style type hints - -Quick Start ------------ - -Installation -~~~~~~~~~~~~ - -To install Arrow, use `pip `_ or `pipenv `_: - -.. code-block:: console - - $ pip install -U arrow - -Example Usage -~~~~~~~~~~~~~ - -.. code-block:: python - - >>> import arrow - >>> arrow.get('2013-05-11T21:23:58.970460+07:00') - - - >>> utc = arrow.utcnow() - >>> utc - - - >>> utc = utc.shift(hours=-1) - >>> utc - - - >>> local = utc.to('US/Pacific') - >>> local - - - >>> local.timestamp() - 1368303838.970460 - - >>> local.format() - '2013-05-11 13:23:58 -07:00' - - >>> local.format('YYYY-MM-DD HH:mm:ss ZZ') - '2013-05-11 13:23:58 -07:00' - - >>> local.humanize() - 'an hour ago' - - >>> local.humanize(locale='ko_kr') - '1시간 전' - -.. end-inclusion-marker-do-not-remove - -Documentation -------------- - -For full documentation, please visit `arrow.readthedocs.io `_. - -Contributing ------------- - -Contributions are welcome for both code and localizations (adding and updating locales). Begin by gaining familiarity with the Arrow library and its features. Then, jump into contributing: - -#. Find an issue or feature to tackle on the `issue tracker `_. Issues marked with the `"good first issue" label `_ may be a great place to start! -#. Fork `this repository `_ on GitHub and begin making changes in a branch. -#. Add a few tests to ensure that the bug was fixed or the feature works as expected. -#. Run the entire test suite and linting checks by running one of the following commands: ``tox && tox -e lint,docs`` (if you have `tox `_ installed) **OR** ``make build39 && make test && make lint`` (if you do not have Python 3.9 installed, replace ``build39`` with the latest Python version on your system). -#. Submit a pull request and await feedback 😃. - -If you have any questions along the way, feel free to ask them `here `_. - -Support Arrow -------------- - -`Open Collective `_ is an online funding platform that provides tools to raise money and share your finances with full transparency. It is the platform of choice for individuals and companies to make one-time or recurring donations directly to the project. If you are interested in making a financial contribution, please visit the `Arrow collective `_. diff --git a/script.module.arrow/lib/arrow/_version.py b/script.module.arrow/lib/arrow/_version.py index 976498ab9..10aa336ce 100644 --- a/script.module.arrow/lib/arrow/_version.py +++ b/script.module.arrow/lib/arrow/_version.py @@ -1 +1 @@ -__version__ = "1.0.3" +__version__ = "1.2.3" diff --git a/script.module.arrow/lib/arrow/api.py b/script.module.arrow/lib/arrow/api.py index 95696f3c1..d8ed24b97 100644 --- a/script.module.arrow/lib/arrow/api.py +++ b/script.module.arrow/lib/arrow/api.py @@ -10,6 +10,7 @@ from typing import Any, List, Optional, Tuple, Type, Union, overload from arrow.arrow import TZ_EXPR, Arrow +from arrow.constants import DEFAULT_LOCALE from arrow.factory import ArrowFactory # internal default factory. @@ -22,7 +23,17 @@ @overload def get( *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, + tzinfo: Optional[TZ_EXPR] = None, + normalize_whitespace: bool = False, +) -> Arrow: + ... # pragma: no cover + + +@overload +def get( + *args: int, + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -43,7 +54,7 @@ def get( Tuple[int, int, int], ], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -55,7 +66,7 @@ def get( __arg1: Union[datetime, date], __arg2: TZ_EXPR, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -67,7 +78,7 @@ def get( __arg1: str, __arg2: Union[str, List[str]], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: diff --git a/script.module.arrow/lib/arrow/arrow.py b/script.module.arrow/lib/arrow/arrow.py index dda7b4056..1ede107f5 100644 --- a/script.module.arrow/lib/arrow/arrow.py +++ b/script.module.arrow/lib/arrow/arrow.py @@ -6,6 +6,7 @@ import calendar +import re import sys from datetime import date from datetime import datetime as dt_datetime @@ -32,6 +33,7 @@ from dateutil.relativedelta import relativedelta from arrow import formatter, locales, parser, util +from arrow.constants import DEFAULT_LOCALE, DEHUMANIZE_LOCALES from arrow.locales import TimeFrameLiteral if sys.version_info < (3, 8): # pragma: no cover @@ -73,6 +75,7 @@ "day", "week", "month", + "quarter", "year", ] @@ -91,7 +94,7 @@ class Arrow: :param second: (optional) the second, Defaults to 0. :param microsecond: (optional) the microsecond. Defaults to 0. :param tzinfo: (optional) A timezone expression. Defaults to UTC. - :param fold: (optional) 0 or 1, used to disambiguate repeated times. Defaults to 0. + :param fold: (optional) 0 or 1, used to disambiguate repeated wall times. Defaults to 0. .. _tz-expr: @@ -130,6 +133,7 @@ class Arrow: _SECS_PER_DAY: Final[int] = 60 * 60 * 24 _SECS_PER_WEEK: Final[int] = 60 * 60 * 24 * 7 _SECS_PER_MONTH: Final[float] = 60 * 60 * 24 * 30.5 + _SECS_PER_QUARTER: Final[float] = 60 * 60 * 24 * 30.5 * 3 _SECS_PER_YEAR: Final[int] = 60 * 60 * 24 * 365 _SECS_MAP: Final[Mapping[TimeFrameLiteral, float]] = { @@ -139,6 +143,7 @@ class Arrow: "day": _SECS_PER_DAY, "week": _SECS_PER_WEEK, "month": _SECS_PER_MONTH, + "quarter": _SECS_PER_QUARTER, "year": _SECS_PER_YEAR, } @@ -245,6 +250,7 @@ def fromtimestamp( :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either. :param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time. + """ if tzinfo is None: @@ -305,12 +311,12 @@ def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> "Arr :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to ``dt``'s timezone, or UTC if naive. - If you only want to replace the timezone of naive datetimes:: + Usage:: >>> dt - datetime.datetime(2013, 5, 5, 0, 0, tzinfo=tzutc()) - >>> arrow.Arrow.fromdatetime(dt, dt.tzinfo or 'US/Pacific') - + datetime.datetime(2021, 4, 7, 13, 48, tzinfo=tzfile('/usr/share/zoneinfo/US/Pacific')) + >>> arrow.Arrow.fromdatetime(dt) + """ @@ -335,10 +341,11 @@ def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> "Arr @classmethod def fromdate(cls, date: date, tzinfo: Optional[TZ_EXPR] = None) -> "Arrow": """Constructs an :class:`Arrow ` object from a ``date`` and optional - replacement timezone. Time values are set to 0. + replacement timezone. All time values are set to 0. :param date: the ``date`` :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to UTC. + """ if tzinfo is None: @@ -354,7 +361,7 @@ def strptime( in the style of ``datetime.strptime``. Optionally replaces the parsed timezone. :param date_str: the date string. - :param fmt: the format string. + :param fmt: the format string using datetime format codes. :param tzinfo: (optional) A :ref:`timezone expression `. Defaults to the parsed timezone if ``fmt`` contains a timezone directive, otherwise UTC. @@ -438,7 +445,7 @@ def range( iterating. As such, either call with naive objects and ``tz``, or aware objects from the same timezone and no ``tz``. - Supported frame values: year, quarter, month, week, day, hour, minute, second. + Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond. Recognized datetime expressions: @@ -504,8 +511,9 @@ def span( count: int = 1, bounds: _BOUNDS = "[)", exact: bool = False, + week_start: int = 1, ) -> Tuple["Arrow", "Arrow"]: - """Returns two new :class:`Arrow ` objects, representing the timespan + """Returns a tuple of two new :class:`Arrow ` objects, representing the timespan of the :class:`Arrow ` object in a given timeframe. :param frame: the timeframe. Can be any ``datetime`` property (day, hour, minute...). @@ -517,6 +525,8 @@ def span( :param exact: (optional) whether to have the start of the timespan begin exactly at the time specified by ``start`` and the end of the timespan truncated so as not to extend beyond ``end``. + :param week_start: (optional) only used in combination with the week timeframe. Follows isoweekday() where + Monday is 1 and Sunday is 7. Supported frame values: year, quarter, month, week, day, hour, minute, second. @@ -537,7 +547,15 @@ def span( >>> arrow.utcnow().span('day', bounds='[]') (, ) + >>> arrow.utcnow().span('week') + (, ) + + >>> arrow.utcnow().span('week', week_start=6) + (, ) + """ + if not 1 <= week_start <= 7: + raise ValueError("week_start argument must be between 1 and 7.") util.validate_bounds(bounds) @@ -563,7 +581,9 @@ def span( floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore if frame_absolute == "week": - floor = floor.shift(days=-(self.isoweekday() - 1)) + # if week_start is greater than self.isoweekday() go back one week by setting delta = 7 + delta = 7 if week_start > self.isoweekday() else 0 + floor = floor.shift(days=-(self.isoweekday() - week_start) - delta) elif frame_absolute == "quarter": floor = floor.shift(months=-((self.month - 1) % 3)) @@ -589,6 +609,7 @@ def floor(self, frame: _T_FRAMES) -> "Arrow": >>> arrow.utcnow().floor('hour') + """ return self.span(frame)[0] @@ -605,6 +626,7 @@ def ceil(self, frame: _T_FRAMES) -> "Arrow": >>> arrow.utcnow().ceil('hour') + """ return self.span(frame)[1] @@ -645,7 +667,7 @@ def span_range( iterating. As such, either call with naive objects and ``tz``, or aware objects from the same timezone and no ``tz``. - Supported frame values: year, quarter, month, week, day, hour, minute, second. + Supported frame values: year, quarter, month, week, day, hour, minute, second, microsecond. Recognized datetime expressions: @@ -737,7 +759,7 @@ def interval( >>> start = datetime(2013, 5, 5, 12, 30) >>> end = datetime(2013, 5, 5, 17, 15) >>> for r in arrow.Arrow.interval('hour', start, end, 2): - ... print r + ... print(r) ... (, ) (, ) @@ -849,8 +871,8 @@ def timestamp(self) -> float: Usage:: - >>> arrow.utcnow().timestamp - 1548260567 + >>> arrow.utcnow().timestamp() + 1616882340.256501 """ @@ -858,7 +880,7 @@ def timestamp(self) -> float: @property def int_timestamp(self) -> int: - """Returns a timestamp representation of the :class:`Arrow ` object, in + """Returns an integer timestamp representation of the :class:`Arrow ` object, in UTC time. Usage:: @@ -872,7 +894,7 @@ def int_timestamp(self) -> int: @property def float_timestamp(self) -> float: - """Returns a floating-point representation of the :class:`Arrow ` + """Returns a floating-point timestamp representation of the :class:`Arrow ` object, in UTC time. Usage:: @@ -886,13 +908,16 @@ def float_timestamp(self) -> float: @property def fold(self) -> int: - """ Returns the ``fold`` value of the :class:`Arrow ` object. """ + """Returns the ``fold`` value of the :class:`Arrow ` object.""" return self._datetime.fold @property def ambiguous(self) -> bool: - """ Returns a boolean indicating whether the :class:`Arrow ` object is ambiguous.""" + """Indicates whether the :class:`Arrow ` object is a repeated wall time in the current + timezone. + + """ return dateutil_tz.datetime_ambiguous(self._datetime) @@ -1067,9 +1092,11 @@ def to(self, tz: TZ_EXPR) -> "Arrow": # string output and formatting - def format(self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = "en_us") -> str: + def format( + self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = DEFAULT_LOCALE + ) -> str: """Returns a string representation of the :class:`Arrow ` object, - formatted according to a format string. + formatted according to the provided format string. :param fmt: the format string. :param locale: the locale to format. @@ -1095,7 +1122,7 @@ def format(self, fmt: str = "YYYY-MM-DD HH:mm:ssZZ", locale: str = "en_us") -> s def humanize( self, other: Union["Arrow", dt_datetime, None] = None, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, only_distance: bool = False, granularity: Union[_GRANULARITY, List[_GRANULARITY]] = "auto", ) -> str: @@ -1103,7 +1130,7 @@ def humanize( :param other: (optional) an :class:`Arrow ` or ``datetime`` object. Defaults to now in the current :class:`Arrow ` object's timezone. - :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en_us'. + :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'. :param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part. :param granularity: (optional) defines the precision of the output. Set it to strings 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings @@ -1221,12 +1248,14 @@ def humanize( delta = sign * delta_second / self._SECS_PER_WEEK elif granularity == "month": delta = sign * delta_second / self._SECS_PER_MONTH + elif granularity == "quarter": + delta = sign * delta_second / self._SECS_PER_QUARTER elif granularity == "year": delta = sign * delta_second / self._SECS_PER_YEAR else: raise ValueError( "Invalid level of granularity. " - "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'." + "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'." ) if trunc(abs(delta)) != 1: @@ -1234,6 +1263,13 @@ def humanize( return locale.describe(granularity, delta, only_distance=only_distance) else: + + if not granularity: + raise ValueError( + "Empty granularity list provided. " + "Please select one or more from 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'." + ) + timeframes: List[Tuple[TimeFrameLiteral, float]] = [] def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: @@ -1251,6 +1287,7 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: delta = float(delta_second) frames: Tuple[TimeFrameLiteral, ...] = ( "year", + "quarter", "month", "week", "day", @@ -1264,7 +1301,7 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: if len(timeframes) < len(granularity): raise ValueError( "Invalid level of granularity. " - "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month' or 'year'." + "Please select between 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter' or 'year'." ) return locale.describe_multi(timeframes, only_distance=only_distance) @@ -1275,6 +1312,147 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float: "Please consider making a contribution to this locale." ) + def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": + """Returns a new :class:`Arrow ` object, that represents + the time difference relative to the attrbiutes of the + :class:`Arrow ` object. + + :param timestring: a ``str`` representing a humanized relative time. + :param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'. + + Usage:: + + >>> arw = arrow.utcnow() + >>> arw + + >>> earlier = arw.dehumanize("2 days ago") + >>> earlier + + + >>> arw = arrow.utcnow() + >>> arw + + >>> later = arw.dehumanize("in a month") + >>> later + + + """ + + # Create a locale object based off given local + locale_obj = locales.get_locale(locale) + + # Check to see if locale is supported + normalized_locale_name = locale.lower().replace("_", "-") + + if normalized_locale_name not in DEHUMANIZE_LOCALES: + raise ValueError( + f"Dehumanize does not currently support the {locale} locale, please consider making a contribution to add support for this locale." + ) + + current_time = self.fromdatetime(self._datetime) + + # Create an object containing the relative time info + time_object_info = dict.fromkeys( + ["seconds", "minutes", "hours", "days", "weeks", "months", "years"], 0 + ) + + # Create an object representing if unit has been seen + unit_visited = dict.fromkeys( + ["now", "seconds", "minutes", "hours", "days", "weeks", "months", "years"], + False, + ) + + # Create a regex pattern object for numbers + num_pattern = re.compile(r"\d+") + + # Search input string for each time unit within locale + for unit, unit_object in locale_obj.timeframes.items(): + + # Need to check the type of unit_object to create the correct dictionary + if isinstance(unit_object, Mapping): + strings_to_search = unit_object + else: + strings_to_search = {unit: str(unit_object)} + + # Search for any matches that exist for that locale's unit. + # Needs to cycle all through strings as some locales have strings that + # could overlap in a regex match, since input validation isn't being performed. + for time_delta, time_string in strings_to_search.items(): + + # Replace {0} with regex \d representing digits + search_string = str(time_string) + search_string = search_string.format(r"\d+") + + # Create search pattern and find within string + pattern = re.compile(rf"(^|\b|\d){search_string}") + match = pattern.search(input_string) + + # If there is no match continue to next iteration + if not match: + continue + + match_string = match.group() + num_match = num_pattern.search(match_string) + + # If no number matches + # Need for absolute value as some locales have signs included in their objects + if not num_match: + change_value = ( + 1 if not time_delta.isnumeric() else abs(int(time_delta)) + ) + else: + change_value = int(num_match.group()) + + # No time to update if now is the unit + if unit == "now": + unit_visited[unit] = True + continue + + # Add change value to the correct unit (incorporates the plurality that exists within timeframe i.e second v.s seconds) + time_unit_to_change = str(unit) + time_unit_to_change += ( + "s" if (str(time_unit_to_change)[-1] != "s") else "" + ) + time_object_info[time_unit_to_change] = change_value + unit_visited[time_unit_to_change] = True + + # Assert error if string does not modify any units + if not any([True for k, v in unit_visited.items() if v]): + raise ValueError( + "Input string not valid. Note: Some locales do not support the week granulairty in Arrow. " + "If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error." + ) + + # Sign logic + future_string = locale_obj.future + future_string = future_string.format(".*") + future_pattern = re.compile(rf"^{future_string}$") + future_pattern_match = future_pattern.findall(input_string) + + past_string = locale_obj.past + past_string = past_string.format(".*") + past_pattern = re.compile(rf"^{past_string}$") + past_pattern_match = past_pattern.findall(input_string) + + # If a string contains the now unit, there will be no relative units, hence the need to check if the now unit + # was visited before raising a ValueError + if past_pattern_match: + sign_val = -1 + elif future_pattern_match: + sign_val = 1 + elif unit_visited["now"]: + sign_val = 0 + else: + raise ValueError( + "Invalid input String. String does not contain any relative time information. " + "String should either represent a time in the future or a time in the past. " + "Ex: 'in 5 seconds' or '5 seconds ago'." + ) + + time_changes = {k: sign_val * v for k, v in time_object_info.items()} + + return current_time.shift(**time_changes) + # query functions def is_between( @@ -1283,8 +1461,8 @@ def is_between( end: "Arrow", bounds: _BOUNDS = "()", ) -> bool: - """Returns a boolean denoting whether the specified date and time is between - the start and end dates and times. + """Returns a boolean denoting whether the :class:`Arrow ` object is between + the start and end limits. :param start: an :class:`Arrow ` object. :param end: an :class:`Arrow ` object. @@ -1622,10 +1800,9 @@ def __le__(self, other: Any) -> bool: return self._datetime <= self._get_datetime(other) # internal methods - @staticmethod def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo: - + """Get normalized tzinfo object from various inputs.""" if tz_expr is None: return dateutil_tz.tzutc() if isinstance(tz_expr, dt_tzinfo): @@ -1640,7 +1817,7 @@ def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo: def _get_datetime( cls, expr: Union["Arrow", dt_datetime, int, float, str] ) -> dt_datetime: - """Get datetime object for a specified expression.""" + """Get datetime object from a specified expression.""" if isinstance(expr, Arrow): return expr.datetime elif isinstance(expr, dt_datetime): @@ -1653,7 +1830,11 @@ def _get_datetime( @classmethod def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]: + """Finds relevant timeframe and steps for use in range and span methods. + + Returns a 3 element tuple in the form (frame, plural frame, step), for example ("day", "days", 1) + """ if name in cls._ATTRS: return name, f"{name}s", 1 elif name[-1] == "s" and name[:-1] in cls._ATTRS: @@ -1682,7 +1863,7 @@ def _get_frames(cls, name: _T_FRAMES) -> Tuple[str, str, int]: @classmethod def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]: - + """Sets default end and limit values for range method.""" if end is None: if limit is None: @@ -1697,6 +1878,7 @@ def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int @staticmethod def _is_last_day_of_month(date: "Arrow") -> bool: + """Returns a boolean indicating whether the datetime is the last day of the month.""" return date.day == calendar.monthrange(date.year, date.month)[1] diff --git a/script.module.arrow/lib/arrow/constants.py b/script.module.arrow/lib/arrow/constants.py index 22ffe55dc..53d163b99 100644 --- a/script.module.arrow/lib/arrow/constants.py +++ b/script.module.arrow/lib/arrow/constants.py @@ -1,3 +1,5 @@ +"""Constants used internally in arrow.""" + import sys from datetime import datetime @@ -19,7 +21,7 @@ # Must get max value of ctime on Windows based on architecture (x32 vs x64) # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-ctime32-ctime64-wctime-wctime32-wctime64 # Note: this may occur on both 32-bit Linux systems (issue #930) along with Windows systems - is_64bits = sys.maxsize > 2 ** 32 + is_64bits = sys.maxsize > 2**32 _MAX_TIMESTAMP = ( datetime(3000, 1, 1, 23, 59, 59, 999999).timestamp() if is_64bits @@ -32,3 +34,144 @@ MAX_ORDINAL: Final[int] = datetime.max.toordinal() MIN_ORDINAL: Final[int] = 1 + +DEFAULT_LOCALE: Final[str] = "en-us" + +# Supported dehumanize locales +DEHUMANIZE_LOCALES = { + "en", + "en-us", + "en-gb", + "en-au", + "en-be", + "en-jp", + "en-za", + "en-ca", + "en-ph", + "fr", + "fr-fr", + "fr-ca", + "it", + "it-it", + "es", + "es-es", + "el", + "el-gr", + "ja", + "ja-jp", + "se", + "se-fi", + "se-no", + "se-se", + "sv", + "sv-se", + "fi", + "fi-fi", + "zh", + "zh-cn", + "zh-tw", + "zh-hk", + "nl", + "nl-nl", + "be", + "be-by", + "pl", + "pl-pl", + "ru", + "ru-ru", + "af", + "bg", + "bg-bg", + "ua", + "uk", + "uk-ua", + "mk", + "mk-mk", + "de", + "de-de", + "de-ch", + "de-at", + "nb", + "nb-no", + "nn", + "nn-no", + "pt", + "pt-pt", + "pt-br", + "tl", + "tl-ph", + "vi", + "vi-vn", + "tr", + "tr-tr", + "az", + "az-az", + "da", + "da-dk", + "ml", + "hi", + "cs", + "cs-cz", + "sk", + "sk-sk", + "fa", + "fa-ir", + "mr", + "ca", + "ca-es", + "ca-ad", + "ca-fr", + "ca-it", + "eo", + "eo-xx", + "bn", + "bn-bd", + "bn-in", + "rm", + "rm-ch", + "ro", + "ro-ro", + "sl", + "sl-si", + "id", + "id-id", + "ne", + "ne-np", + "ee", + "et", + "sw", + "sw-ke", + "sw-tz", + "la", + "la-va", + "lt", + "lt-lt", + "ms", + "ms-my", + "ms-bn", + "or", + "or-in", + "lb", + "lb-lu", + "zu", + "zu-za", + "sq", + "sq-al", + "ta", + "ta-in", + "ta-lk", + "ur", + "ur-pk", + "ka", + "ka-ge", + "kk", + "kk-kz", + # "lo", + # "lo-la", + "am", + "am-et", + "hy-am", + "hy", + "uz", + "uz-uz", +} diff --git a/script.module.arrow/lib/arrow/factory.py b/script.module.arrow/lib/arrow/factory.py index 231510f58..aad4af8bd 100644 --- a/script.module.arrow/lib/arrow/factory.py +++ b/script.module.arrow/lib/arrow/factory.py @@ -9,6 +9,7 @@ import calendar from datetime import date, datetime from datetime import tzinfo as dt_tzinfo +from decimal import Decimal from time import struct_time from typing import Any, List, Optional, Tuple, Type, Union, overload @@ -16,6 +17,7 @@ from arrow import parser from arrow.arrow import TZ_EXPR, Arrow +from arrow.constants import DEFAULT_LOCALE from arrow.util import is_timestamp, iso_to_gregorian @@ -36,7 +38,7 @@ def __init__(self, type: Type[Arrow] = Arrow) -> None: def get( self, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -57,7 +59,7 @@ def get( Tuple[int, int, int], ], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -69,7 +71,7 @@ def get( __arg1: Union[datetime, date], __arg2: TZ_EXPR, *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -81,7 +83,7 @@ def get( __arg1: str, __arg2: Union[str, List[str]], *, - locale: str = "en_us", + locale: str = DEFAULT_LOCALE, tzinfo: Optional[TZ_EXPR] = None, normalize_whitespace: bool = False, ) -> Arrow: @@ -90,7 +92,7 @@ def get( def get(self, *args: Any, **kwargs: Any) -> Arrow: """Returns an :class:`Arrow ` object based on flexible inputs. - :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en_us'. + :param locale: (optional) a ``str`` specifying a locale for the parser. Defaults to 'en-us'. :param tzinfo: (optional) a :ref:`timezone expression ` or tzinfo object. Replaces the timezone unless using an input form that is explicitly UTC or specifies the timezone in a positional argument. Defaults to UTC. @@ -107,11 +109,6 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: >>> arrow.get() - **None** to also get current UTC time:: - - >>> arrow.get(None) - - **One** :class:`Arrow ` object, to get a copy. >>> arw = arrow.utcnow() @@ -189,7 +186,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: >>> arrow.get('2013-05-05 12:30:45', ['MM/DD/YYYY', 'YYYY-MM-DD HH:mm:ss']) - **Three or more** arguments, as for the constructor of a ``datetime``:: + **Three or more** arguments, as for the direct constructor of an ``Arrow`` object:: >>> arrow.get(2013, 5, 5, 12, 30, 45) @@ -197,7 +194,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: """ arg_count = len(args) - locale = kwargs.pop("locale", "en_us") + locale = kwargs.pop("locale", DEFAULT_LOCALE) tz = kwargs.get("tzinfo", None) normalize_whitespace = kwargs.pop("normalize_whitespace", False) @@ -209,60 +206,62 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: if len(kwargs) == 1 and tz is None: arg_count = 3 - # () -> now, @ utc. + # () -> now, @ tzinfo or utc if arg_count == 0: if isinstance(tz, str): tz = parser.TzinfoParser.parse(tz) - return self.type.now(tz) + return self.type.now(tzinfo=tz) if isinstance(tz, dt_tzinfo): - return self.type.now(tz) + return self.type.now(tzinfo=tz) return self.type.utcnow() if arg_count == 1: arg = args[0] + if isinstance(arg, Decimal): + arg = float(arg) # (None) -> raises an exception if arg is None: raise TypeError("Cannot parse argument of type None.") - # try (int, float) -> from timestamp with tz + # try (int, float) -> from timestamp @ tzinfo elif not isinstance(arg, str) and is_timestamp(arg): if tz is None: # set to UTC by default tz = dateutil_tz.tzutc() return self.type.fromtimestamp(arg, tzinfo=tz) - # (Arrow) -> from the object's datetime. + # (Arrow) -> from the object's datetime @ tzinfo elif isinstance(arg, Arrow): - return self.type.fromdatetime(arg.datetime) + return self.type.fromdatetime(arg.datetime, tzinfo=tz) - # (datetime) -> from datetime. + # (datetime) -> from datetime @ tzinfo elif isinstance(arg, datetime): - return self.type.fromdatetime(arg) + return self.type.fromdatetime(arg, tzinfo=tz) - # (date) -> from date. + # (date) -> from date @ tzinfo elif isinstance(arg, date): - return self.type.fromdate(arg) + return self.type.fromdate(arg, tzinfo=tz) - # (tzinfo) -> now, @ tzinfo. + # (tzinfo) -> now @ tzinfo elif isinstance(arg, dt_tzinfo): - return self.type.now(arg) + return self.type.now(tzinfo=arg) - # (str) -> parse. + # (str) -> parse @ tzinfo elif isinstance(arg, str): dt = parser.DateTimeParser(locale).parse_iso(arg, normalize_whitespace) - return self.type.fromdatetime(dt, tz) + return self.type.fromdatetime(dt, tzinfo=tz) # (struct_time) -> from struct_time elif isinstance(arg, struct_time): return self.type.utcfromtimestamp(calendar.timegm(arg)) - # (iso calendar) -> convert then from date + # (iso calendar) -> convert then from date @ tzinfo elif isinstance(arg, tuple) and len(arg) == 3: d = iso_to_gregorian(*arg) - return self.type.fromdate(d) + return self.type.fromdate(d, tzinfo=tz) else: raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.") @@ -273,9 +272,9 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: if isinstance(arg_1, datetime): - # (datetime, tzinfo/str) -> fromdatetime replace tzinfo. + # (datetime, tzinfo/str) -> fromdatetime @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): - return self.type.fromdatetime(arg_1, arg_2) + return self.type.fromdatetime(arg_1, tzinfo=arg_2) else: raise TypeError( f"Cannot parse two arguments of types 'datetime', {type(arg_2)!r}." @@ -283,7 +282,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: elif isinstance(arg_1, date): - # (date, tzinfo/str) -> fromdate replace tzinfo. + # (date, tzinfo/str) -> fromdate @ tzinfo if isinstance(arg_2, (dt_tzinfo, str)): return self.type.fromdate(arg_1, tzinfo=arg_2) else: @@ -291,7 +290,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: f"Cannot parse two arguments of types 'date', {type(arg_2)!r}." ) - # (str, format) -> parse. + # (str, format) -> parse @ tzinfo elif isinstance(arg_1, str) and isinstance(arg_2, (str, list)): dt = parser.DateTimeParser(locale).parse( args[0], args[1], normalize_whitespace @@ -303,7 +302,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow: f"Cannot parse two arguments of types {type(arg_1)!r} and {type(arg_2)!r}." ) - # 3+ args -> datetime-like via constructor. + # 3+ args -> datetime-like via constructor else: return self.type(*args, **kwargs) diff --git a/script.module.arrow/lib/arrow/formatter.py b/script.module.arrow/lib/arrow/formatter.py index 14eb44a20..728bea1aa 100644 --- a/script.module.arrow/lib/arrow/formatter.py +++ b/script.module.arrow/lib/arrow/formatter.py @@ -1,3 +1,5 @@ +"""Provides the :class:`Arrow ` class, an improved formatter for datetimes.""" + import re import sys from datetime import datetime, timedelta @@ -6,6 +8,7 @@ from dateutil import tz as dateutil_tz from arrow import locales +from arrow.constants import DEFAULT_LOCALE if sys.version_info < (3, 8): # pragma: no cover from typing_extensions import Final @@ -37,7 +40,7 @@ class DateTimeFormatter: locale: locales.Locale - def __init__(self, locale: str = "en_us") -> None: + def __init__(self, locale: str = DEFAULT_LOCALE) -> None: self.locale = locales.get_locale(locale) diff --git a/script.module.arrow/lib/arrow/locales.py b/script.module.arrow/lib/arrow/locales.py index d801f9d63..3627497f5 100644 --- a/script.module.arrow/lib/arrow/locales.py +++ b/script.module.arrow/lib/arrow/locales.py @@ -1,4 +1,5 @@ -import inspect +"""Provides internationalization for arrow in over 60 languages and dialects.""" + import sys from math import trunc from typing import ( @@ -34,19 +35,18 @@ "weeks", "month", "months", + "quarter", + "quarters", "year", "years", - "2-hours", - "2-days", - "2-weeks", - "2-months", - "2-years", ] _TimeFrameElements = Union[ str, Sequence[str], Mapping[str, str], Mapping[str, Sequence[str]] ] +_locale_map: Dict[str, Type["Locale"]] = {} + def get_locale(name: str) -> "Locale": """Returns an appropriate :class:`Locale ` @@ -56,10 +56,11 @@ def get_locale(name: str) -> "Locale": """ - locale_cls = _locales.get(name.lower()) + normalized_locale_name = name.lower().replace("_", "-") + locale_cls = _locale_map.get(normalized_locale_name) if locale_cls is None: - raise ValueError(f"Unsupported locale {name!r}.") + raise ValueError(f"Unsupported locale {normalized_locale_name!r}.") return locale_cls() @@ -79,11 +80,8 @@ def get_locale_by_class_name(name: str) -> "Locale": return locale_cls() -# base locale type. - - class Locale: - """ Represents locale-specific data and functionality. """ + """Represents locale-specific data and functionality.""" names: ClassVar[List[str]] = [] @@ -101,6 +99,8 @@ class Locale: "weeks": "", "month": "", "months": "", + "quarter": "", + "quarters": "", "year": "", "years": "", } @@ -121,6 +121,13 @@ class Locale: _month_name_to_ordinal: Optional[Dict[str, int]] + def __init_subclass__(cls, **kwargs: Any) -> None: + for locale_name in cls.names: + if locale_name in _locale_map: + raise LookupError(f"Duplicated locale name: {locale_name}") + + _locale_map[locale_name.lower().replace("_", "-")] = cls + def __init__(self) -> None: self._month_name_to_ordinal = None @@ -138,7 +145,7 @@ def describe( :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords """ - humanized = self._format_timeframe(timeframe, delta) + humanized = self._format_timeframe(timeframe, trunc(delta)) if not only_distance: humanized = self._format_relative(humanized, timeframe, delta) @@ -156,14 +163,24 @@ def describe_multi( """ parts = [ - self._format_timeframe(timeframe, delta) for timeframe, delta in timeframes + self._format_timeframe(timeframe, trunc(delta)) + for timeframe, delta in timeframes ] if self.and_word: parts.insert(-1, self.and_word) humanized = " ".join(parts) if not only_distance: - humanized = self._format_relative(humanized, *timeframes[-1]) + # Needed to determine the correct relative string to use + timeframe_value = 0 + + for _unit_name, unit_value in timeframes: + if trunc(unit_value) != 0: + timeframe_value = trunc(unit_value) + break + + # Note it doesn't matter the timeframe unit we use on the call, only the value + humanized = self._format_relative(humanized, "seconds", timeframe_value) return humanized @@ -258,9 +275,7 @@ def _ordinal_number(self, n: int) -> str: def _name_to_ordinal(self, lst: Sequence[str]) -> Dict[str, int]: return {elem.lower(): i for i, elem in enumerate(lst[1:], 1)} - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: # TODO: remove cast return cast(str, self.timeframes[timeframe]).format(trunc(abs(delta))) @@ -279,21 +294,17 @@ def _format_relative( return direction.format(humanized) -# base locale type implementations. - - class EnglishLocale(Locale): - names = [ "en", - "en_us", - "en_gb", - "en_au", - "en_be", - "en_jp", - "en_za", - "en_ca", - "en_ph", + "en-us", + "en-gb", + "en-au", + "en-be", + "en-jp", + "en-za", + "en-ca", + "en-ph", ] past = "{0} ago" @@ -314,6 +325,8 @@ class EnglishLocale(Locale): "weeks": "{0} weeks", "month": "a month", "months": "{0} months", + "quarter": "a quarter", + "quarters": "{0} quarters", "year": "a year", "years": "{0} years", } @@ -397,7 +410,7 @@ def describe( class ItalianLocale(Locale): - names = ["it", "it_it"] + names = ["it", "it-it"] past = "{0} fa" future = "tra {0}" and_word = "e" @@ -470,7 +483,7 @@ def _ordinal_number(self, n: int) -> str: class SpanishLocale(Locale): - names = ["es", "es_es"] + names = ["es", "es-es"] past = "hace {0}" future = "en {0}" and_word = "y" @@ -545,7 +558,6 @@ def _ordinal_number(self, n: int) -> str: class FrenchBaseLocale(Locale): - past = "il y a {0}" future = "dans {0}" and_word = "et" @@ -607,8 +619,7 @@ def _ordinal_number(self, n: int) -> str: class FrenchLocale(FrenchBaseLocale, Locale): - - names = ["fr", "fr_fr"] + names = ["fr", "fr-fr"] month_abbreviations = [ "", @@ -628,8 +639,7 @@ class FrenchLocale(FrenchBaseLocale, Locale): class FrenchCanadianLocale(FrenchBaseLocale, Locale): - - names = ["fr_ca"] + names = ["fr-ca"] month_abbreviations = [ "", @@ -649,8 +659,7 @@ class FrenchCanadianLocale(FrenchBaseLocale, Locale): class GreekLocale(Locale): - - names = ["el", "el_gr"] + names = ["el", "el-gr"] past = "{0} πριν" future = "σε {0}" @@ -666,6 +675,8 @@ class GreekLocale(Locale): "hours": "{0} ώρες", "day": "μία μέρα", "days": "{0} μέρες", + "week": "μία εβδομάδα", + "weeks": "{0} εβδομάδες", "month": "ένα μήνα", "months": "{0} μήνες", "year": "ένα χρόνο", @@ -717,8 +728,7 @@ class GreekLocale(Locale): class JapaneseLocale(Locale): - - names = ["ja", "ja_jp"] + names = ["ja", "ja-jp"] past = "{0}前" future = "{0}後" @@ -778,8 +788,7 @@ class JapaneseLocale(Locale): class SwedishLocale(Locale): - - names = ["sv", "sv_se"] + names = ["sv", "sv-se"] past = "för {0} sen" future = "om {0}" @@ -848,8 +857,7 @@ class SwedishLocale(Locale): class FinnishLocale(Locale): - - names = ["fi", "fi_fi"] + names = ["fi", "fi-fi"] # The finnish grammar is very complex, and its hard to convert # 1-to-1 to something like English. @@ -857,20 +865,20 @@ class FinnishLocale(Locale): past = "{0} sitten" future = "{0} kuluttua" - timeframes: ClassVar[Mapping[TimeFrameLiteral, List[str]]] = { - "now": ["juuri nyt", "juuri nyt"], - "second": ["sekunti", "sekunti"], - "seconds": ["{0} muutama sekunti", "{0} muutaman sekunnin"], - "minute": ["minuutti", "minuutin"], - "minutes": ["{0} minuuttia", "{0} minuutin"], - "hour": ["tunti", "tunnin"], - "hours": ["{0} tuntia", "{0} tunnin"], - "day": ["päivä", "päivä"], - "days": ["{0} päivää", "{0} päivän"], - "month": ["kuukausi", "kuukauden"], - "months": ["{0} kuukautta", "{0} kuukauden"], - "year": ["vuosi", "vuoden"], - "years": ["{0} vuotta", "{0} vuoden"], + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "juuri nyt", + "second": "sekunti", + "seconds": {"past": "{0} muutama sekunti", "future": "{0} muutaman sekunnin"}, + "minute": {"past": "minuutti", "future": "minuutin"}, + "minutes": {"past": "{0} minuuttia", "future": "{0} minuutin"}, + "hour": {"past": "tunti", "future": "tunnin"}, + "hours": {"past": "{0} tuntia", "future": "{0} tunnin"}, + "day": "päivä", + "days": {"past": "{0} päivää", "future": "{0} päivän"}, + "month": {"past": "kuukausi", "future": "kuukauden"}, + "months": {"past": "{0} kuukautta", "future": "{0} kuukauden"}, + "year": {"past": "vuosi", "future": "vuoden"}, + "years": {"past": "{0} vuotta", "future": "{0} vuoden"}, } # Months and days are lowercase in Finnish @@ -919,41 +927,30 @@ class FinnishLocale(Locale): day_abbreviations = ["", "ma", "ti", "ke", "to", "pe", "la", "su"] - # TODO: Fix return type - def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: Union[float, int]) -> Tuple[str, str]: # type: ignore - return ( - self.timeframes[timeframe][0].format(abs(delta)), - self.timeframes[timeframe][1].format(abs(delta)), - ) - - def _format_relative( - self, - humanized: str, - timeframe: TimeFrameLiteral, - delta: Union[float, int], - ) -> str: - if timeframe == "now": - return humanized[0] + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] - direction = self.past if delta < 0 else self.future - which = 0 if delta < 0 else 1 + if isinstance(form, Mapping): + if delta < 0: + form = form["past"] + else: + form = form["future"] - return direction.format(humanized[which]) + return form.format(abs(delta)) def _ordinal_number(self, n: int) -> str: return f"{n}." class ChineseCNLocale(Locale): - - names = ["zh", "zh_cn"] + names = ["zh", "zh-cn"] past = "{0}前" future = "{0}后" timeframes = { "now": "刚才", - "second": "一秒", + "second": "1秒", "seconds": "{0}秒", "minute": "1分钟", "minutes": "{0}分钟", @@ -961,7 +958,7 @@ class ChineseCNLocale(Locale): "hours": "{0}小时", "day": "1天", "days": "{0}天", - "week": "一周", + "week": "1周", "weeks": "{0}周", "month": "1个月", "months": "{0}个月", @@ -1005,8 +1002,7 @@ class ChineseCNLocale(Locale): class ChineseTWLocale(Locale): - - names = ["zh_tw"] + names = ["zh-tw"] past = "{0}前" future = "{0}後" @@ -1066,8 +1062,7 @@ class ChineseTWLocale(Locale): class HongKongLocale(Locale): - - names = ["zh_hk"] + names = ["zh-hk"] past = "{0}前" future = "{0}後" @@ -1126,8 +1121,7 @@ class HongKongLocale(Locale): class KoreanLocale(Locale): - - names = ["ko", "ko_kr"] + names = ["ko", "ko-kr"] past = "{0} 전" future = "{0} 후" @@ -1222,8 +1216,7 @@ def _format_relative( # derived locale types & implementations. class DutchLocale(Locale): - - names = ["nl", "nl_nl"] + names = ["nl", "nl-nl"] past = "{0} geleden" future = "over {0}" @@ -1293,46 +1286,55 @@ class DutchLocale(Locale): class SlavicBaseLocale(Locale): - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] delta = abs(delta) - if isinstance(form, list): + if isinstance(form, Mapping): if delta % 10 == 1 and delta % 100 != 11: - form = form[0] + form = form["singular"] elif 2 <= delta % 10 <= 4 and (delta % 100 < 10 or delta % 100 >= 20): - form = form[1] + form = form["dual"] else: - form = form[2] + form = form["plural"] return form.format(delta) class BelarusianLocale(SlavicBaseLocale): - - names = ["be", "be_by"] + names = ["be", "be-by"] past = "{0} таму" future = "праз {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "зараз", "second": "секунду", "seconds": "{0} некалькі секунд", "minute": "хвіліну", - "minutes": ["{0} хвіліну", "{0} хвіліны", "{0} хвілін"], + "minutes": { + "singular": "{0} хвіліну", + "dual": "{0} хвіліны", + "plural": "{0} хвілін", + }, "hour": "гадзіну", - "hours": ["{0} гадзіну", "{0} гадзіны", "{0} гадзін"], + "hours": { + "singular": "{0} гадзіну", + "dual": "{0} гадзіны", + "plural": "{0} гадзін", + }, "day": "дзень", - "days": ["{0} дзень", "{0} дні", "{0} дзён"], + "days": {"singular": "{0} дзень", "dual": "{0} дні", "plural": "{0} дзён"}, "month": "месяц", - "months": ["{0} месяц", "{0} месяцы", "{0} месяцаў"], + "months": { + "singular": "{0} месяц", + "dual": "{0} месяцы", + "plural": "{0} месяцаў", + }, "year": "год", - "years": ["{0} год", "{0} гады", "{0} гадоў"], + "years": {"singular": "{0} год", "dual": "{0} гады", "plural": "{0} гадоў"}, } month_names = [ @@ -1380,30 +1382,49 @@ class BelarusianLocale(SlavicBaseLocale): class PolishLocale(SlavicBaseLocale): - - names = ["pl", "pl_pl"] + names = ["pl", "pl-pl"] past = "{0} temu" future = "za {0}" # The nouns should be in genitive case (Polish: "dopełniacz") # in order to correctly form `past` & `future` expressions. - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "teraz", "second": "sekundę", - "seconds": ["{0} sekund", "{0} sekundy", "{0} sekund"], + "seconds": { + "singular": "{0} sekund", + "dual": "{0} sekundy", + "plural": "{0} sekund", + }, "minute": "minutę", - "minutes": ["{0} minut", "{0} minuty", "{0} minut"], + "minutes": { + "singular": "{0} minut", + "dual": "{0} minuty", + "plural": "{0} minut", + }, "hour": "godzinę", - "hours": ["{0} godzin", "{0} godziny", "{0} godzin"], + "hours": { + "singular": "{0} godzin", + "dual": "{0} godziny", + "plural": "{0} godzin", + }, "day": "dzień", "days": "{0} dni", "week": "tydzień", - "weeks": ["{0} tygodni", "{0} tygodnie", "{0} tygodni"], + "weeks": { + "singular": "{0} tygodni", + "dual": "{0} tygodnie", + "plural": "{0} tygodni", + }, "month": "miesiąc", - "months": ["{0} miesięcy", "{0} miesiące", "{0} miesięcy"], + "months": { + "singular": "{0} miesięcy", + "dual": "{0} miesiące", + "plural": "{0} miesięcy", + }, "year": "rok", - "years": ["{0} lat", "{0} lata", "{0} lat"], + "years": {"singular": "{0} lat", "dual": "{0} lata", "plural": "{0} lat"}, } month_names = [ @@ -1451,28 +1472,49 @@ class PolishLocale(SlavicBaseLocale): class RussianLocale(SlavicBaseLocale): - - names = ["ru", "ru_ru"] + names = ["ru", "ru-ru"] past = "{0} назад" future = "через {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сейчас", - "second": "Второй", - "seconds": "{0} несколько секунд", + "second": "секунда", + "seconds": { + "singular": "{0} секунду", + "dual": "{0} секунды", + "plural": "{0} секунд", + }, "minute": "минуту", - "minutes": ["{0} минуту", "{0} минуты", "{0} минут"], + "minutes": { + "singular": "{0} минуту", + "dual": "{0} минуты", + "plural": "{0} минут", + }, "hour": "час", - "hours": ["{0} час", "{0} часа", "{0} часов"], + "hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часов"}, "day": "день", - "days": ["{0} день", "{0} дня", "{0} дней"], + "days": {"singular": "{0} день", "dual": "{0} дня", "plural": "{0} дней"}, "week": "неделю", - "weeks": ["{0} неделю", "{0} недели", "{0} недель"], + "weeks": { + "singular": "{0} неделю", + "dual": "{0} недели", + "plural": "{0} недель", + }, "month": "месяц", - "months": ["{0} месяц", "{0} месяца", "{0} месяцев"], + "months": { + "singular": "{0} месяц", + "dual": "{0} месяца", + "plural": "{0} месяцев", + }, + "quarter": "квартал", + "quarters": { + "singular": "{0} квартал", + "dual": "{0} квартала", + "plural": "{0} кварталов", + }, "year": "год", - "years": ["{0} год", "{0} года", "{0} лет"], + "years": {"singular": "{0} год", "dual": "{0} года", "plural": "{0} лет"}, } month_names = [ @@ -1520,8 +1562,7 @@ class RussianLocale(SlavicBaseLocale): class AfrikaansLocale(Locale): - - names = ["af", "af_nl"] + names = ["af", "af-nl"] past = "{0} gelede" future = "in {0}" @@ -1587,26 +1628,37 @@ class AfrikaansLocale(Locale): class BulgarianLocale(SlavicBaseLocale): - - names = ["bg", "bg_BG"] + names = ["bg", "bg-bg"] past = "{0} назад" future = "напред {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сега", "second": "секунда", "seconds": "{0} няколко секунди", "minute": "минута", - "minutes": ["{0} минута", "{0} минути", "{0} минути"], + "minutes": { + "singular": "{0} минута", + "dual": "{0} минути", + "plural": "{0} минути", + }, "hour": "час", - "hours": ["{0} час", "{0} часа", "{0} часа"], + "hours": {"singular": "{0} час", "dual": "{0} часа", "plural": "{0} часа"}, "day": "ден", - "days": ["{0} ден", "{0} дни", "{0} дни"], + "days": {"singular": "{0} ден", "dual": "{0} дни", "plural": "{0} дни"}, "month": "месец", - "months": ["{0} месец", "{0} месеца", "{0} месеца"], + "months": { + "singular": "{0} месец", + "dual": "{0} месеца", + "plural": "{0} месеца", + }, "year": "година", - "years": ["{0} година", "{0} години", "{0} години"], + "years": { + "singular": "{0} година", + "dual": "{0} години", + "plural": "{0} години", + }, } month_names = [ @@ -1654,26 +1706,37 @@ class BulgarianLocale(SlavicBaseLocale): class UkrainianLocale(SlavicBaseLocale): - - names = ["ua", "uk_ua"] + names = ["ua", "uk", "uk-ua"] past = "{0} тому" future = "за {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "зараз", "second": "секунда", "seconds": "{0} кілька секунд", "minute": "хвилину", - "minutes": ["{0} хвилину", "{0} хвилини", "{0} хвилин"], + "minutes": { + "singular": "{0} хвилину", + "dual": "{0} хвилини", + "plural": "{0} хвилин", + }, "hour": "годину", - "hours": ["{0} годину", "{0} години", "{0} годин"], + "hours": { + "singular": "{0} годину", + "dual": "{0} години", + "plural": "{0} годин", + }, "day": "день", - "days": ["{0} день", "{0} дні", "{0} днів"], + "days": {"singular": "{0} день", "dual": "{0} дні", "plural": "{0} днів"}, "month": "місяць", - "months": ["{0} місяць", "{0} місяці", "{0} місяців"], + "months": { + "singular": "{0} місяць", + "dual": "{0} місяці", + "plural": "{0} місяців", + }, "year": "рік", - "years": ["{0} рік", "{0} роки", "{0} років"], + "years": {"singular": "{0} рік", "dual": "{0} роки", "plural": "{0} років"}, } month_names = [ @@ -1721,27 +1784,47 @@ class UkrainianLocale(SlavicBaseLocale): class MacedonianLocale(SlavicBaseLocale): - names = ["mk", "mk_mk"] + names = ["mk", "mk-mk"] past = "пред {0}" future = "за {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, List[str]]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "сега", "second": "една секунда", - "seconds": ["{0} секунда", "{0} секунди", "{0} секунди"], + "seconds": { + "singular": "{0} секунда", + "dual": "{0} секунди", + "plural": "{0} секунди", + }, "minute": "една минута", - "minutes": ["{0} минута", "{0} минути", "{0} минути"], + "minutes": { + "singular": "{0} минута", + "dual": "{0} минути", + "plural": "{0} минути", + }, "hour": "еден саат", - "hours": ["{0} саат", "{0} саати", "{0} саати"], + "hours": {"singular": "{0} саат", "dual": "{0} саати", "plural": "{0} саати"}, "day": "еден ден", - "days": ["{0} ден", "{0} дена", "{0} дена"], + "days": {"singular": "{0} ден", "dual": "{0} дена", "plural": "{0} дена"}, "week": "една недела", - "weeks": ["{0} недела", "{0} недели", "{0} недели"], + "weeks": { + "singular": "{0} недела", + "dual": "{0} недели", + "plural": "{0} недели", + }, "month": "еден месец", - "months": ["{0} месец", "{0} месеци", "{0} месеци"], + "months": { + "singular": "{0} месец", + "dual": "{0} месеци", + "plural": "{0} месеци", + }, "year": "една година", - "years": ["{0} година", "{0} години", "{0} години"], + "years": { + "singular": "{0} година", + "dual": "{0} години", + "plural": "{0} години", + }, } meridians = {"am": "дп", "pm": "пп", "AM": "претпладне", "PM": "попладне"} @@ -1800,7 +1883,6 @@ class MacedonianLocale(SlavicBaseLocale): class GermanBaseLocale(Locale): - past = "vor {0}" future = "in {0}" and_word = "und" @@ -1906,18 +1988,15 @@ def describe( class GermanLocale(GermanBaseLocale, Locale): - - names = ["de", "de_de"] + names = ["de", "de-de"] class SwissLocale(GermanBaseLocale, Locale): - - names = ["de_ch"] + names = ["de-ch"] class AustrianLocale(GermanBaseLocale, Locale): - - names = ["de_at"] + names = ["de-at"] month_names = [ "", @@ -1937,8 +2016,7 @@ class AustrianLocale(GermanBaseLocale, Locale): class NorwegianLocale(Locale): - - names = ["nb", "nb_no"] + names = ["nb", "nb-no"] past = "for {0} siden" future = "om {0}" @@ -1953,6 +2031,8 @@ class NorwegianLocale(Locale): "hours": "{0} timer", "day": "en dag", "days": "{0} dager", + "week": "en uke", + "weeks": "{0} uker", "month": "en måned", "months": "{0} måneder", "year": "ett år", @@ -2002,10 +2082,12 @@ class NorwegianLocale(Locale): ] day_abbreviations = ["", "ma", "ti", "on", "to", "fr", "lø", "sø"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." + class NewNorwegianLocale(Locale): - - names = ["nn", "nn_no"] + names = ["nn", "nn-no"] past = "for {0} sidan" future = "om {0}" @@ -2020,7 +2102,9 @@ class NewNorwegianLocale(Locale): "hours": "{0} timar", "day": "ein dag", "days": "{0} dagar", - "month": "en månad", + "week": "ei veke", + "weeks": "{0} veker", + "month": "ein månad", "months": "{0} månader", "year": "eitt år", "years": "{0} år", @@ -2069,9 +2153,12 @@ class NewNorwegianLocale(Locale): ] day_abbreviations = ["", "må", "ty", "on", "to", "fr", "la", "su"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." + class PortugueseLocale(Locale): - names = ["pt", "pt_pt"] + names = ["pt", "pt-pt"] past = "há {0}" future = "em {0}" @@ -2140,14 +2227,13 @@ class PortugueseLocale(Locale): class BrazilianPortugueseLocale(PortugueseLocale): - names = ["pt_br"] + names = ["pt-br"] past = "faz {0}" class TagalogLocale(Locale): - - names = ["tl", "tl_ph"] + names = ["tl", "tl-ph"] past = "nakaraang {0}" future = "{0} mula ngayon" @@ -2220,8 +2306,7 @@ def _ordinal_number(self, n: int) -> str: class VietnameseLocale(Locale): - - names = ["vi", "vi_vn"] + names = ["vi", "vi-vn"] past = "{0} trước" future = "{0} nữa" @@ -2289,11 +2374,11 @@ class VietnameseLocale(Locale): class TurkishLocale(Locale): - - names = ["tr", "tr_tr"] + names = ["tr", "tr-tr"] past = "{0} önce" future = "{0} sonra" + and_word = "ve" timeframes = { "now": "şimdi", @@ -2305,12 +2390,16 @@ class TurkishLocale(Locale): "hours": "{0} saat", "day": "bir gün", "days": "{0} gün", + "week": "bir hafta", + "weeks": "{0} hafta", "month": "bir ay", "months": "{0} ay", - "year": "yıl", + "year": "bir yıl", "years": "{0} yıl", } + meridians = {"am": "öö", "pm": "ös", "AM": "ÖÖ", "PM": "ÖS"} + month_names = [ "", "Ocak", @@ -2356,15 +2445,14 @@ class TurkishLocale(Locale): class AzerbaijaniLocale(Locale): - - names = ["az", "az_az"] + names = ["az", "az-az"] past = "{0} əvvəl" future = "{0} sonra" timeframes = { "now": "indi", - "second": "saniyə", + "second": "bir saniyə", "seconds": "{0} saniyə", "minute": "bir dəqiqə", "minutes": "{0} dəqiqə", @@ -2372,9 +2460,11 @@ class AzerbaijaniLocale(Locale): "hours": "{0} saat", "day": "bir gün", "days": "{0} gün", + "week": "bir həftə", + "weeks": "{0} həftə", "month": "bir ay", "months": "{0} ay", - "year": "il", + "year": "bir il", "years": "{0} il", } @@ -2425,23 +2515,23 @@ class AzerbaijaniLocale(Locale): class ArabicLocale(Locale): names = [ "ar", - "ar_ae", - "ar_bh", - "ar_dj", - "ar_eg", - "ar_eh", - "ar_er", - "ar_km", - "ar_kw", - "ar_ly", - "ar_om", - "ar_qa", - "ar_sa", - "ar_sd", - "ar_so", - "ar_ss", - "ar_td", - "ar_ye", + "ar-ae", + "ar-bh", + "ar-dj", + "ar-eg", + "ar-eh", + "ar-er", + "ar-km", + "ar-kw", + "ar-ly", + "ar-om", + "ar-qa", + "ar-sa", + "ar-sd", + "ar-so", + "ar-ss", + "ar-td", + "ar-ye", ] past = "منذ {0}" @@ -2450,17 +2540,17 @@ class ArabicLocale(Locale): timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "الآن", "second": "ثانية", - "seconds": {"double": "ثانيتين", "ten": "{0} ثوان", "higher": "{0} ثانية"}, + "seconds": {"2": "ثانيتين", "ten": "{0} ثوان", "higher": "{0} ثانية"}, "minute": "دقيقة", - "minutes": {"double": "دقيقتين", "ten": "{0} دقائق", "higher": "{0} دقيقة"}, + "minutes": {"2": "دقيقتين", "ten": "{0} دقائق", "higher": "{0} دقيقة"}, "hour": "ساعة", - "hours": {"double": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"}, + "hours": {"2": "ساعتين", "ten": "{0} ساعات", "higher": "{0} ساعة"}, "day": "يوم", - "days": {"double": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"}, + "days": {"2": "يومين", "ten": "{0} أيام", "higher": "{0} يوم"}, "month": "شهر", - "months": {"double": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"}, + "months": {"2": "شهرين", "ten": "{0} أشهر", "higher": "{0} شهر"}, "year": "سنة", - "years": {"double": "سنتين", "ten": "{0} سنوات", "higher": "{0} سنة"}, + "years": {"2": "سنتين", "ten": "{0} سنوات", "higher": "{0} سنة"}, } month_names = [ @@ -2506,14 +2596,12 @@ class ArabicLocale(Locale): ] day_abbreviations = ["", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] delta = abs(delta) if isinstance(form, Mapping): if delta == 2: - form = form["double"] + form = form["2"] elif 2 < delta <= 10: form = form["ten"] else: @@ -2523,7 +2611,7 @@ def _format_timeframe( class LevantArabicLocale(ArabicLocale): - names = ["ar_iq", "ar_jo", "ar_lb", "ar_ps", "ar_sy"] + names = ["ar-iq", "ar-jo", "ar-lb", "ar-ps", "ar-sy"] month_names = [ "", "كانون الثاني", @@ -2557,7 +2645,7 @@ class LevantArabicLocale(ArabicLocale): class AlgeriaTunisiaArabicLocale(ArabicLocale): - names = ["ar_tn", "ar_dz"] + names = ["ar-tn", "ar-dz"] month_names = [ "", "جانفي", @@ -2591,7 +2679,7 @@ class AlgeriaTunisiaArabicLocale(ArabicLocale): class MauritaniaArabicLocale(ArabicLocale): - names = ["ar_mr"] + names = ["ar-mr"] month_names = [ "", "يناير", @@ -2625,7 +2713,7 @@ class MauritaniaArabicLocale(ArabicLocale): class MoroccoArabicLocale(ArabicLocale): - names = ["ar_ma"] + names = ["ar-ma"] month_names = [ "", "يناير", @@ -2659,37 +2747,42 @@ class MoroccoArabicLocale(ArabicLocale): class IcelandicLocale(Locale): - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] - if delta < 0: - form = form[0] - elif delta > 0: - form = form[1] - # FIXME: handle when delta is 0 - return form.format(abs(delta)) # type: ignore + if isinstance(form, Mapping): + if delta < 0: + form = form["past"] + elif delta > 0: + form = form["future"] + else: + raise ValueError( + "Icelandic Locale does not support units with a delta of zero. " + "Please consider making a contribution to fix this issue." + ) + # FIXME: handle when delta is 0 + + return form.format(abs(delta)) - names = ["is", "is_is"] + names = ["is", "is-is"] past = "fyrir {0} síðan" future = "eftir {0}" - timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Tuple[str, str], str]]] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "rétt í þessu", - "second": ("sekúndu", "sekúndu"), - "seconds": ("{0} nokkrum sekúndum", "nokkrar sekúndur"), - "minute": ("einni mínútu", "eina mínútu"), - "minutes": ("{0} mínútum", "{0} mínútur"), - "hour": ("einum tíma", "einn tíma"), - "hours": ("{0} tímum", "{0} tíma"), - "day": ("einum degi", "einn dag"), - "days": ("{0} dögum", "{0} daga"), - "month": ("einum mánuði", "einn mánuð"), - "months": ("{0} mánuðum", "{0} mánuði"), - "year": ("einu ári", "eitt ár"), - "years": ("{0} árum", "{0} ár"), + "second": {"past": "sekúndu", "future": "sekúndu"}, + "seconds": {"past": "{0} nokkrum sekúndum", "future": "nokkrar sekúndur"}, + "minute": {"past": "einni mínútu", "future": "eina mínútu"}, + "minutes": {"past": "{0} mínútum", "future": "{0} mínútur"}, + "hour": {"past": "einum tíma", "future": "einn tíma"}, + "hours": {"past": "{0} tímum", "future": "{0} tíma"}, + "day": {"past": "einum degi", "future": "einn dag"}, + "days": {"past": "{0} dögum", "future": "{0} daga"}, + "month": {"past": "einum mánuði", "future": "einn mánuð"}, + "months": {"past": "{0} mánuðum", "future": "{0} mánuði"}, + "year": {"past": "einu ári", "future": "eitt ár"}, + "years": {"past": "{0} árum", "future": "{0} ár"}, } meridians = {"am": "f.h.", "pm": "e.h.", "AM": "f.h.", "PM": "e.h."} @@ -2739,23 +2832,24 @@ def _format_timeframe( class DanishLocale(Locale): - - names = ["da", "da_dk"] + names = ["da", "da-dk"] past = "for {0} siden" - future = "efter {0}" + future = "om {0}" and_word = "og" timeframes = { "now": "lige nu", "second": "et sekund", - "seconds": "{0} et par sekunder", + "seconds": "{0} sekunder", "minute": "et minut", "minutes": "{0} minutter", "hour": "en time", "hours": "{0} timer", "day": "en dag", "days": "{0} dage", + "week": "en uge", + "weeks": "{0} uger", "month": "en måned", "months": "{0} måneder", "year": "et år", @@ -2805,9 +2899,11 @@ class DanishLocale(Locale): ] day_abbreviations = ["", "man", "tir", "ons", "tor", "fre", "lør", "søn"] + def _ordinal_number(self, n: int) -> str: + return f"{n}." -class MalayalamLocale(Locale): +class MalayalamLocale(Locale): names = ["ml"] past = "{0} മുമ്പ്" @@ -2881,8 +2977,7 @@ class MalayalamLocale(Locale): class HindiLocale(Locale): - - names = ["hi"] + names = ["hi", "hi-in"] past = "{0} पहले" future = "{0} बाद" @@ -2923,7 +3018,7 @@ class HindiLocale(Locale): month_abbreviations = [ "", "जन", - "फ़र", + "फ़र", "मार्च", "अप्रै", "मई", @@ -2950,26 +3045,59 @@ class HindiLocale(Locale): class CzechLocale(Locale): - names = ["cs", "cs_cz"] + names = ["cs", "cs-cz"] - timeframes: ClassVar[ - Mapping[TimeFrameLiteral, Union[Mapping[str, Union[List[str], str]], str]] - ] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teď", - "second": {"past": "vteřina", "future": "vteřina", "zero": "vteřina"}, - "seconds": {"past": "{0} sekundami", "future": ["{0} sekundy", "{0} sekund"]}, - "minute": {"past": "minutou", "future": "minutu", "zero": "{0} minut"}, - "minutes": {"past": "{0} minutami", "future": ["{0} minuty", "{0} minut"]}, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodin"}, - "hours": {"past": "{0} hodinami", "future": ["{0} hodiny", "{0} hodin"]}, - "day": {"past": "dnem", "future": "den", "zero": "{0} dnů"}, - "days": {"past": "{0} dny", "future": ["{0} dny", "{0} dnů"]}, - "week": {"past": "týdnem", "future": "týden", "zero": "{0} týdnů"}, - "weeks": {"past": "{0} týdny", "future": ["{0} týdny", "{0} týdnů"]}, - "month": {"past": "měsícem", "future": "měsíc", "zero": "{0} měsíců"}, - "months": {"past": "{0} měsíci", "future": ["{0} měsíce", "{0} měsíců"]}, - "year": {"past": "rokem", "future": "rok", "zero": "{0} let"}, - "years": {"past": "{0} lety", "future": ["{0} roky", "{0} let"]}, + "second": {"past": "vteřina", "future": "vteřina"}, + "seconds": { + "zero": "vteřina", + "past": "{0} sekundami", + "future-singular": "{0} sekundy", + "future-paucal": "{0} sekund", + }, + "minute": {"past": "minutou", "future": "minutu"}, + "minutes": { + "zero": "{0} minut", + "past": "{0} minutami", + "future-singular": "{0} minuty", + "future-paucal": "{0} minut", + }, + "hour": {"past": "hodinou", "future": "hodinu"}, + "hours": { + "zero": "{0} hodin", + "past": "{0} hodinami", + "future-singular": "{0} hodiny", + "future-paucal": "{0} hodin", + }, + "day": {"past": "dnem", "future": "den"}, + "days": { + "zero": "{0} dnů", + "past": "{0} dny", + "future-singular": "{0} dny", + "future-paucal": "{0} dnů", + }, + "week": {"past": "týdnem", "future": "týden"}, + "weeks": { + "zero": "{0} týdnů", + "past": "{0} týdny", + "future-singular": "{0} týdny", + "future-paucal": "{0} týdnů", + }, + "month": {"past": "měsícem", "future": "měsíc"}, + "months": { + "zero": "{0} měsíců", + "past": "{0} měsíci", + "future-singular": "{0} měsíce", + "future-paucal": "{0} měsíců", + }, + "year": {"past": "rokem", "future": "rok"}, + "years": { + "zero": "{0} let", + "past": "{0} lety", + "future-singular": "{0} roky", + "future-paucal": "{0} let", + }, } past = "Před {0}" @@ -3018,9 +3146,7 @@ class CzechLocale(Locale): ] day_abbreviations = ["", "po", "út", "st", "čt", "pá", "so", "ne"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: """Czech aware time frame format function, takes into account the differences between past and future forms.""" abs_delta = abs(delta) @@ -3031,44 +3157,77 @@ def _format_timeframe( if delta == 0: key = "zero" # And *never* use 0 in the singular! - elif delta > 0: - key = "future" - else: + elif delta < 0: key = "past" - form: Union[List[str], str] = form[key] - - if isinstance(form, list): - if 2 <= abs_delta % 10 <= 4 and ( + else: + # Needed since both regular future and future-singular and future-paucal cases + if "future-singular" not in form: + key = "future" + elif 2 <= abs_delta % 10 <= 4 and ( abs_delta % 100 < 10 or abs_delta % 100 >= 20 ): - form = form[0] + key = "future-singular" else: - form = form[1] + key = "future-paucal" + form: str = form[key] return form.format(abs_delta) class SlovakLocale(Locale): - names = ["sk", "sk_sk"] + names = ["sk", "sk-sk"] - timeframes: ClassVar[ - Mapping[TimeFrameLiteral, Union[Mapping[str, Union[List[str], str]], str]] - ] = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "Teraz", - "second": {"past": "sekundou", "future": "sekundu", "zero": "{0} sekúnd"}, - "seconds": {"past": "{0} sekundami", "future": ["{0} sekundy", "{0} sekúnd"]}, - "minute": {"past": "minútou", "future": "minútu", "zero": "{0} minút"}, - "minutes": {"past": "{0} minútami", "future": ["{0} minúty", "{0} minút"]}, - "hour": {"past": "hodinou", "future": "hodinu", "zero": "{0} hodín"}, - "hours": {"past": "{0} hodinami", "future": ["{0} hodiny", "{0} hodín"]}, - "day": {"past": "dňom", "future": "deň", "zero": "{0} dní"}, - "days": {"past": "{0} dňami", "future": ["{0} dni", "{0} dní"]}, - "week": {"past": "týždňom", "future": "týždeň", "zero": "{0} týždňov"}, - "weeks": {"past": "{0} týždňami", "future": ["{0} týždne", "{0} týždňov"]}, - "month": {"past": "mesiacom", "future": "mesiac", "zero": "{0} mesiacov"}, - "months": {"past": "{0} mesiacmi", "future": ["{0} mesiace", "{0} mesiacov"]}, - "year": {"past": "rokom", "future": "rok", "zero": "{0} rokov"}, - "years": {"past": "{0} rokmi", "future": ["{0} roky", "{0} rokov"]}, + "second": {"past": "sekundou", "future": "sekundu"}, + "seconds": { + "zero": "{0} sekúnd", + "past": "{0} sekundami", + "future-singular": "{0} sekundy", + "future-paucal": "{0} sekúnd", + }, + "minute": {"past": "minútou", "future": "minútu"}, + "minutes": { + "zero": "{0} minút", + "past": "{0} minútami", + "future-singular": "{0} minúty", + "future-paucal": "{0} minút", + }, + "hour": {"past": "hodinou", "future": "hodinu"}, + "hours": { + "zero": "{0} hodín", + "past": "{0} hodinami", + "future-singular": "{0} hodiny", + "future-paucal": "{0} hodín", + }, + "day": {"past": "dňom", "future": "deň"}, + "days": { + "zero": "{0} dní", + "past": "{0} dňami", + "future-singular": "{0} dni", + "future-paucal": "{0} dní", + }, + "week": {"past": "týždňom", "future": "týždeň"}, + "weeks": { + "zero": "{0} týždňov", + "past": "{0} týždňami", + "future-singular": "{0} týždne", + "future-paucal": "{0} týždňov", + }, + "month": {"past": "mesiacom", "future": "mesiac"}, + "months": { + "zero": "{0} mesiacov", + "past": "{0} mesiacmi", + "future-singular": "{0} mesiace", + "future-paucal": "{0} mesiacov", + }, + "year": {"past": "rokom", "future": "rok"}, + "years": { + "zero": "{0} rokov", + "past": "{0} rokmi", + "future-singular": "{0} roky", + "future-paucal": "{0} rokov", + }, } past = "Pred {0}" @@ -3118,9 +3277,7 @@ class SlovakLocale(Locale): ] day_abbreviations = ["", "po", "ut", "st", "št", "pi", "so", "ne"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: """Slovak aware time frame format function, takes into account the differences between past and future forms.""" abs_delta = abs(delta) @@ -3131,26 +3288,24 @@ def _format_timeframe( if delta == 0: key = "zero" # And *never* use 0 in the singular! - elif delta > 0: - key = "future" - else: + elif delta < 0: key = "past" - form: Union[List[str], str] = form[key] - - if isinstance(form, list): - if 2 <= abs_delta % 10 <= 4 and ( + else: + if "future-singular" not in form: + key = "future" + elif 2 <= abs_delta % 10 <= 4 and ( abs_delta % 100 < 10 or abs_delta % 100 >= 20 ): - form = form[0] + key = "future-singular" else: - form = form[1] + key = "future-paucal" + form: str = form[key] return form.format(abs_delta) class FarsiLocale(Locale): - - names = ["fa", "fa_ir"] + names = ["fa", "fa-ir"] past = "{0} قبل" future = "در {0}" @@ -3223,34 +3378,28 @@ class FarsiLocale(Locale): class HebrewLocale(Locale): - - names = ["he", "he_IL"] + names = ["he", "he-il"] past = "לפני {0}" future = "בעוד {0}" and_word = "ו" - timeframes = { + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { "now": "הרגע", "second": "שנייה", "seconds": "{0} שניות", "minute": "דקה", "minutes": "{0} דקות", "hour": "שעה", - "hours": "{0} שעות", - "2-hours": "שעתיים", + "hours": {"2": "שעתיים", "ten": "{0} שעות", "higher": "{0} שעות"}, "day": "יום", - "days": "{0} ימים", - "2-days": "יומיים", + "days": {"2": "יומיים", "ten": "{0} ימים", "higher": "{0} יום"}, "week": "שבוע", - "weeks": "{0} שבועות", - "2-weeks": "שבועיים", + "weeks": {"2": "שבועיים", "ten": "{0} שבועות", "higher": "{0} שבועות"}, "month": "חודש", - "months": "{0} חודשים", - "2-months": "חודשיים", + "months": {"2": "חודשיים", "ten": "{0} חודשים", "higher": "{0} חודשים"}, "year": "שנה", - "years": "{0} שנים", - "2-years": "שנתיים", + "years": {"2": "שנתיים", "ten": "{0} שנים", "higher": "{0} שנה"}, } meridians = { @@ -3294,20 +3443,18 @@ class HebrewLocale(Locale): day_names = ["", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"] day_abbreviations = ["", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳", "א׳"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: - """Hebrew couple of aware""" - couple = f"2-{timeframe}" - single = timeframe.rstrip("s") - if abs(delta) == 2 and couple in self.timeframes: - key = couple - elif abs(delta) == 1 and single in self.timeframes: - key = single - else: - key = timeframe + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if delta == 2: + form = form["2"] + elif delta == 0 or 2 < delta <= 10: + form = form["ten"] + else: + form = form["higher"] - return self.timeframes[key].format(trunc(abs(delta))) + return form.format(delta) def describe_multi( self, @@ -3323,7 +3470,7 @@ def describe_multi( humanized = "" for index, (timeframe, delta) in enumerate(timeframes): - last_humanized = self._format_timeframe(timeframe, delta) + last_humanized = self._format_timeframe(timeframe, trunc(delta)) if index == 0: humanized = last_humanized elif index == len(timeframes) - 1: # Must have at least 2 items @@ -3335,13 +3482,12 @@ def describe_multi( humanized += ", " + last_humanized if not only_distance: - humanized = self._format_relative(humanized, timeframe, delta) + humanized = self._format_relative(humanized, timeframe, trunc(delta)) return humanized class MarathiLocale(Locale): - names = ["mr"] past = "{0} आधी" @@ -3409,20 +3555,8 @@ class MarathiLocale(Locale): day_abbreviations = ["", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि", "रवि"] -def _map_locales() -> Dict[str, Type[Locale]]: - - locales: Dict[str, Type[Locale]] = {} - - for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): - if issubclass(cls, Locale): # pragma: no branch - for name in cls.names: - locales[name.lower()] = cls - - return locales - - class CatalanLocale(Locale): - names = ["ca", "ca_es", "ca_ad", "ca_fr", "ca_it"] + names = ["ca", "ca-es", "ca-ad", "ca-fr", "ca-it"] past = "Fa {0}" future = "En {0}" and_word = "i" @@ -3431,7 +3565,7 @@ class CatalanLocale(Locale): "now": "Ara mateix", "second": "un segon", "seconds": "{0} segons", - "minute": "1 minut", + "minute": "un minut", "minutes": "{0} minuts", "hour": "una hora", "hours": "{0} hores", @@ -3496,7 +3630,7 @@ class CatalanLocale(Locale): class BasqueLocale(Locale): - names = ["eu", "eu_eu"] + names = ["eu", "eu-eu"] past = "duela {0}" future = "{0}" # I don't know what's the right phrase in Basque for the future. @@ -3560,8 +3694,7 @@ class BasqueLocale(Locale): class HungarianLocale(Locale): - - names = ["hu", "hu_hu"] + names = ["hu", "hu-hu"] past = "{0} ezelőtt" future = "{0} múlva" @@ -3627,9 +3760,7 @@ class HungarianLocale(Locale): meridians = {"am": "de", "pm": "du", "AM": "DE", "PM": "DU"} - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] if isinstance(form, Mapping): @@ -3642,7 +3773,7 @@ def _format_timeframe( class EsperantoLocale(Locale): - names = ["eo", "eo_xx"] + names = ["eo", "eo-xx"] past = "antaŭ {0}" future = "post {0}" @@ -3714,11 +3845,10 @@ def _ordinal_number(self, n: int) -> str: class ThaiLocale(Locale): + names = ["th", "th-th"] - names = ["th", "th_th"] - - past = "{0}{1}ที่ผ่านมา" - future = "ในอีก{1}{0}" + past = "{0} ที่ผ่านมา" + future = "ในอีก {0}" timeframes = { "now": "ขณะนี้", @@ -3793,15 +3923,126 @@ def _format_relative( """Thai normally doesn't have any space between words""" if timeframe == "now": return humanized - space = "" if timeframe == "seconds" else " " + direction = self.past if delta < 0 else self.future + relative_string = direction.format(humanized) - return direction.format(humanized, space) + if timeframe == "seconds": + relative_string = relative_string.replace(" ", "") + return relative_string + + +class LaotianLocale(Locale): + + names = ["lo", "lo-la"] + + past = "{0} ກ່ອນຫນ້ານີ້" + future = "ໃນ {0}" + + timeframes = { + "now": "ດຽວນີ້", + "second": "ວິນາທີ", + "seconds": "{0} ວິນາທີ", + "minute": "ນາທີ", + "minutes": "{0} ນາທີ", + "hour": "ຊົ່ວໂມງ", + "hours": "{0} ຊົ່ວໂມງ", + "day": "ມື້", + "days": "{0} ມື້", + "week": "ອາທິດ", + "weeks": "{0} ອາທິດ", + "month": "ເດືອນ", + "months": "{0} ເດືອນ", + "year": "ປີ", + "years": "{0} ປີ", + } + + month_names = [ + "", + "ມັງກອນ", # mangkon + "ກຸມພາ", # kumpha + "ມີນາ", # mina + "ເມສາ", # mesa + "ພຶດສະພາ", # phudsapha + "ມິຖຸນາ", # mithuna + "ກໍລະກົດ", # kolakod + "ສິງຫາ", # singha + "ກັນຍາ", # knaia + "ຕຸລາ", # tula + "ພະຈິກ", # phachik + "ທັນວາ", # thanuaa + ] + month_abbreviations = [ + "", + "ມັງກອນ", + "ກຸມພາ", + "ມີນາ", + "ເມສາ", + "ພຶດສະພາ", + "ມິຖຸນາ", + "ກໍລະກົດ", + "ສິງຫາ", + "ກັນຍາ", + "ຕຸລາ", + "ພະຈິກ", + "ທັນວາ", + ] + + day_names = [ + "", + "ວັນຈັນ", # vanchan + "ວັນອັງຄານ", # vnoangkhan + "ວັນພຸດ", # vanphud + "ວັນພະຫັດ", # vanphahad + "ວັນ​ສຸກ", # vansuk + "ວັນເສົາ", # vansao + "ວັນອາທິດ", # vnoathid + ] + day_abbreviations = [ + "", + "ວັນຈັນ", + "ວັນອັງຄານ", + "ວັນພຸດ", + "ວັນພະຫັດ", + "ວັນ​ສຸກ", + "ວັນເສົາ", + "ວັນອາທິດ", + ] + + BE_OFFSET = 543 + + def year_full(self, year: int) -> str: + """Lao always use Buddhist Era (BE) which is CE + 543""" + year += self.BE_OFFSET + return f"{year:04d}" + + def year_abbreviation(self, year: int) -> str: + """Lao always use Buddhist Era (BE) which is CE + 543""" + year += self.BE_OFFSET + return f"{year:04d}"[2:] + + def _format_relative( + self, + humanized: str, + timeframe: TimeFrameLiteral, + delta: Union[float, int], + ) -> str: + """Lao normally doesn't have any space between words""" + if timeframe == "now": + return humanized + + direction = self.past if delta < 0 else self.future + relative_string = direction.format(humanized) + + if timeframe == "seconds": + relative_string = relative_string.replace(" ", "") + + return relative_string -class BengaliLocale(Locale): - names = ["bn", "bn_bd", "bn_in"] +class BengaliLocale(Locale): + names = ["bn", "bn-bd", "bn-in"] past = "{0} আগে" future = "{0} পরে" @@ -3827,7 +4068,7 @@ class BengaliLocale(Locale): month_names = [ "", "জানুয়ারি", - "ফেব্রুয়ারি", + "ফেব্রুয়ারি", "মার্চ", "এপ্রিল", "মে", @@ -3873,7 +4114,7 @@ def _ordinal_number(self, n: int) -> str: if n in [1, 5, 7, 8, 9, 10]: return f"{n}ম" if n in [2, 3]: - return f"{n}য়" + return f"{n}য়" if n == 4: return f"{n}র্থ" if n == 6: @@ -3881,8 +4122,7 @@ def _ordinal_number(self, n: int) -> str: class RomanshLocale(Locale): - - names = ["rm", "rm_ch"] + names = ["rm", "rm-ch"] past = "avant {0}" future = "en {0}" @@ -3950,7 +4190,7 @@ class RomanshLocale(Locale): class RomanianLocale(Locale): - names = ["ro", "ro_ro"] + names = ["ro", "ro-ro"] past = "{0} în urmă" future = "peste {0}" @@ -4017,7 +4257,7 @@ class RomanianLocale(Locale): class SlovenianLocale(Locale): - names = ["sl", "sl_si"] + names = ["sl", "sl-si"] past = "pred {0}" future = "čez {0}" @@ -4088,8 +4328,7 @@ class SlovenianLocale(Locale): class IndonesianLocale(Locale): - - names = ["id", "id_id"] + names = ["id", "id-id"] past = "{0} yang lalu" future = "dalam {0}" @@ -4105,8 +4344,12 @@ class IndonesianLocale(Locale): "hours": "{0} jam", "day": "1 hari", "days": "{0} hari", + "week": "1 minggu", + "weeks": "{0} minggu", "month": "1 bulan", "months": "{0} bulan", + "quarter": "1 kuartal", + "quarters": "{0} kuartal", "year": "1 tahun", "years": "{0} tahun", } @@ -4160,7 +4403,7 @@ class IndonesianLocale(Locale): class NepaliLocale(Locale): - names = ["ne", "ne_np"] + names = ["ne", "ne-np"] past = "{0} पहिले" future = "{0} पछी" @@ -4178,7 +4421,7 @@ class NepaliLocale(Locale): "month": "एक महिना", "months": "{0} महिना", "year": "एक बर्ष", - "years": "बर्ष", + "years": "{0} बर्ष", } meridians = {"am": "पूर्वाह्न", "pm": "अपरान्ह", "AM": "पूर्वाह्न", "PM": "अपरान्ह"} @@ -4294,9 +4537,7 @@ class EstonianLocale(Locale): ] day_abbreviations = ["", "Esm", "Teis", "Kolm", "Nelj", "Re", "Lau", "Püh"] - def _format_timeframe( - self, timeframe: TimeFrameLiteral, delta: Union[float, int] - ) -> str: + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: form = self.timeframes[timeframe] if delta > 0: _form = form["future"] @@ -4305,12 +4546,91 @@ def _format_timeframe( return _form.format(abs(delta)) -class SwahiliLocale(Locale): +class LatvianLocale(Locale): + names = ["lv", "lv-lv"] + + past = "pirms {0}" + future = "pēc {0}" + and_word = "un" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "tagad", + "second": "sekundes", + "seconds": "{0} sekundēm", + "minute": "minūtes", + "minutes": "{0} minūtēm", + "hour": "stundas", + "hours": "{0} stundām", + "day": "dienas", + "days": "{0} dienām", + "week": "nedēļas", + "weeks": "{0} nedēļām", + "month": "mēneša", + "months": "{0} mēnešiem", + "year": "gada", + "years": "{0} gadiem", + } + + month_names = [ + "", + "janvāris", + "februāris", + "marts", + "aprīlis", + "maijs", + "jūnijs", + "jūlijs", + "augusts", + "septembris", + "oktobris", + "novembris", + "decembris", + ] + + month_abbreviations = [ + "", + "jan", + "feb", + "marts", + "apr", + "maijs", + "jūnijs", + "jūlijs", + "aug", + "sept", + "okt", + "nov", + "dec", + ] + + day_names = [ + "", + "pirmdiena", + "otrdiena", + "trešdiena", + "ceturtdiena", + "piektdiena", + "sestdiena", + "svētdiena", + ] + + day_abbreviations = [ + "", + "pi", + "ot", + "tr", + "ce", + "pi", + "se", + "sv", + ] + +class SwahiliLocale(Locale): names = [ "sw", - "sw_ke", - "sw_tz", + "sw-ke", + "sw-tz", ] past = "{0} iliyopita" @@ -4390,4 +4710,1766 @@ class SwahiliLocale(Locale): ] -_locales: Dict[str, Type[Locale]] = _map_locales() +class CroatianLocale(Locale): + names = ["hr", "hr-hr"] + + past = "prije {0}" + future = "za {0}" + and_word = "i" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "upravo sad", + "second": "sekundu", + "seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"}, + "minute": "minutu", + "minutes": {"double": "{0} minute", "higher": "{0} minuta"}, + "hour": "sat", + "hours": {"double": "{0} sata", "higher": "{0} sati"}, + "day": "jedan dan", + "days": {"double": "{0} dana", "higher": "{0} dana"}, + "week": "tjedan", + "weeks": {"double": "{0} tjedna", "higher": "{0} tjedana"}, + "month": "mjesec", + "months": {"double": "{0} mjeseca", "higher": "{0} mjeseci"}, + "year": "godinu", + "years": {"double": "{0} godine", "higher": "{0} godina"}, + } + + month_names = [ + "", + "siječanj", + "veljača", + "ožujak", + "travanj", + "svibanj", + "lipanj", + "srpanj", + "kolovoz", + "rujan", + "listopad", + "studeni", + "prosinac", + ] + + month_abbreviations = [ + "", + "siječ", + "velj", + "ožuj", + "trav", + "svib", + "lip", + "srp", + "kol", + "ruj", + "list", + "stud", + "pros", + ] + + day_names = [ + "", + "ponedjeljak", + "utorak", + "srijeda", + "četvrtak", + "petak", + "subota", + "nedjelja", + ] + + day_abbreviations = [ + "", + "po", + "ut", + "sr", + "če", + "pe", + "su", + "ne", + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if 1 < delta <= 4: + form = form["double"] + else: + form = form["higher"] + + return form.format(delta) + + +class LatinLocale(Locale): + names = ["la", "la-va"] + + past = "ante {0}" + future = "in {0}" + and_word = "et" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "nunc", + "second": "secundum", + "seconds": "{0} secundis", + "minute": "minutam", + "minutes": "{0} minutis", + "hour": "horam", + "hours": "{0} horas", + "day": "diem", + "days": "{0} dies", + "week": "hebdomadem", + "weeks": "{0} hebdomades", + "month": "mensem", + "months": "{0} mensis", + "year": "annum", + "years": "{0} annos", + } + + month_names = [ + "", + "Ianuarius", + "Februarius", + "Martius", + "Aprilis", + "Maius", + "Iunius", + "Iulius", + "Augustus", + "September", + "October", + "November", + "December", + ] + + month_abbreviations = [ + "", + "Ian", + "Febr", + "Mart", + "Apr", + "Mai", + "Iun", + "Iul", + "Aug", + "Sept", + "Oct", + "Nov", + "Dec", + ] + + day_names = [ + "", + "dies Lunae", + "dies Martis", + "dies Mercurii", + "dies Iovis", + "dies Veneris", + "dies Saturni", + "dies Solis", + ] + + day_abbreviations = [ + "", + "dies Lunae", + "dies Martis", + "dies Mercurii", + "dies Iovis", + "dies Veneris", + "dies Saturni", + "dies Solis", + ] + + +class LithuanianLocale(Locale): + names = ["lt", "lt-lt"] + + past = "prieš {0}" + future = "po {0}" + and_word = "ir" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "dabar", + "second": "sekundės", + "seconds": "{0} sekundžių", + "minute": "minutės", + "minutes": "{0} minučių", + "hour": "valandos", + "hours": "{0} valandų", + "day": "dieną", + "days": "{0} dienų", + "week": "savaitės", + "weeks": "{0} savaičių", + "month": "mėnesio", + "months": "{0} mėnesių", + "year": "metų", + "years": "{0} metų", + } + + month_names = [ + "", + "sausis", + "vasaris", + "kovas", + "balandis", + "gegužė", + "birželis", + "liepa", + "rugpjūtis", + "rugsėjis", + "spalis", + "lapkritis", + "gruodis", + ] + + month_abbreviations = [ + "", + "saus", + "vas", + "kovas", + "bal", + "geg", + "birž", + "liepa", + "rugp", + "rugs", + "spalis", + "lapkr", + "gr", + ] + + day_names = [ + "", + "pirmadienis", + "antradienis", + "trečiadienis", + "ketvirtadienis", + "penktadienis", + "šeštadienis", + "sekmadienis", + ] + + day_abbreviations = [ + "", + "pi", + "an", + "tr", + "ke", + "pe", + "še", + "se", + ] + + +class MalayLocale(Locale): + names = ["ms", "ms-my", "ms-bn"] + + past = "{0} yang lalu" + future = "dalam {0}" + and_word = "dan" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "sekarang", + "second": "saat", + "seconds": "{0} saat", + "minute": "minit", + "minutes": "{0} minit", + "hour": "jam", + "hours": "{0} jam", + "day": "hari", + "days": "{0} hari", + "week": "minggu", + "weeks": "{0} minggu", + "month": "bulan", + "months": "{0} bulan", + "year": "tahun", + "years": "{0} tahun", + } + + month_names = [ + "", + "Januari", + "Februari", + "Mac", + "April", + "Mei", + "Jun", + "Julai", + "Ogos", + "September", + "Oktober", + "November", + "Disember", + ] + + month_abbreviations = [ + "", + "Jan.", + "Feb.", + "Mac", + "Apr.", + "Mei", + "Jun", + "Julai", + "Og.", + "Sept.", + "Okt.", + "Nov.", + "Dis.", + ] + + day_names = [ + "", + "Isnin", + "Selasa", + "Rabu", + "Khamis", + "Jumaat", + "Sabtu", + "Ahad", + ] + + day_abbreviations = [ + "", + "Isnin", + "Selasa", + "Rabu", + "Khamis", + "Jumaat", + "Sabtu", + "Ahad", + ] + + +class MalteseLocale(Locale): + names = ["mt", "mt-mt"] + + past = "{0} ilu" + future = "fi {0}" + and_word = "u" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "issa", + "second": "sekonda", + "seconds": "{0} sekondi", + "minute": "minuta", + "minutes": "{0} minuti", + "hour": "siegħa", + "hours": {"dual": "{0} sagħtejn", "plural": "{0} sigħat"}, + "day": "jum", + "days": {"dual": "{0} jumejn", "plural": "{0} ijiem"}, + "week": "ġimgħa", + "weeks": {"dual": "{0} ġimagħtejn", "plural": "{0} ġimgħat"}, + "month": "xahar", + "months": {"dual": "{0} xahrejn", "plural": "{0} xhur"}, + "year": "sena", + "years": {"dual": "{0} sentejn", "plural": "{0} snin"}, + } + + month_names = [ + "", + "Jannar", + "Frar", + "Marzu", + "April", + "Mejju", + "Ġunju", + "Lulju", + "Awwissu", + "Settembru", + "Ottubru", + "Novembru", + "Diċembru", + ] + + month_abbreviations = [ + "", + "Jan", + "Fr", + "Mar", + "Apr", + "Mejju", + "Ġun", + "Lul", + "Aw", + "Sett", + "Ott", + "Nov", + "Diċ", + ] + + day_names = [ + "", + "It-Tnejn", + "It-Tlieta", + "L-Erbgħa", + "Il-Ħamis", + "Il-Ġimgħa", + "Is-Sibt", + "Il-Ħadd", + ] + + day_abbreviations = [ + "", + "T", + "TL", + "E", + "Ħ", + "Ġ", + "S", + "Ħ", + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if delta == 2: + form = form["dual"] + else: + form = form["plural"] + + return form.format(delta) + + +class SamiLocale(Locale): + names = ["se", "se-fi", "se-no", "se-se"] + + past = "{0} dassái" + future = "{0} " # NOTE: couldn't find preposition for Sami here, none needed? + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "dál", + "second": "sekunda", + "seconds": "{0} sekundda", + "minute": "minuhta", + "minutes": "{0} minuhta", + "hour": "diimmu", + "hours": "{0} diimmu", + "day": "beaivvi", + "days": "{0} beaivvi", + "week": "vahku", + "weeks": "{0} vahku", + "month": "mánu", + "months": "{0} mánu", + "year": "jagi", + "years": "{0} jagi", + } + + month_names = [ + "", + "Ođđajagimánnu", + "Guovvamánnu", + "Njukčamánnu", + "Cuoŋománnu", + "Miessemánnu", + "Geassemánnu", + "Suoidnemánnu", + "Borgemánnu", + "Čakčamánnu", + "Golggotmánnu", + "Skábmamánnu", + "Juovlamánnu", + ] + + month_abbreviations = [ + "", + "Ođđajagimánnu", + "Guovvamánnu", + "Njukčamánnu", + "Cuoŋománnu", + "Miessemánnu", + "Geassemánnu", + "Suoidnemánnu", + "Borgemánnu", + "Čakčamánnu", + "Golggotmánnu", + "Skábmamánnu", + "Juovlamánnu", + ] + + day_names = [ + "", + "Mánnodat", + "Disdat", + "Gaskavahkku", + "Duorastat", + "Bearjadat", + "Lávvordat", + "Sotnabeaivi", + ] + + day_abbreviations = [ + "", + "Mánnodat", + "Disdat", + "Gaskavahkku", + "Duorastat", + "Bearjadat", + "Lávvordat", + "Sotnabeaivi", + ] + + +class OdiaLocale(Locale): + names = ["or", "or-in"] + + past = "{0} ପୂର୍ବେ" + future = "{0} ପରେ" + + timeframes = { + "now": "ବର୍ତ୍ତମାନ", + "second": "ଏକ ସେକେଣ୍ଡ", + "seconds": "{0} ସେକେଣ୍ଡ", + "minute": "ଏକ ମିନଟ", + "minutes": "{0} ମିନଟ", + "hour": "ଏକ ଘଣ୍ଟା", + "hours": "{0} ଘଣ୍ଟା", + "day": "ଏକ ଦିନ", + "days": "{0} ଦିନ", + "month": "ଏକ ମାସ", + "months": "{0} ମାସ ", + "year": "ଏକ ବର୍ଷ", + "years": "{0} ବର୍ଷ", + } + + meridians = {"am": "ପୂର୍ବାହ୍ନ", "pm": "ଅପରାହ୍ନ", "AM": "ପୂର୍ବାହ୍ନ", "PM": "ଅପରାହ୍ନ"} + + month_names = [ + "", + "ଜାନୁଆରୀ", + "ଫେବୃଆରୀ", + "ମାର୍ଚ୍ଚ୍", + "ଅପ୍ରେଲ", + "ମଇ", + "ଜୁନ୍", + "ଜୁଲାଇ", + "ଅଗଷ୍ଟ", + "ସେପ୍ଟେମ୍ବର", + "ଅକ୍ଟୋବର୍", + "ନଭେମ୍ବର୍", + "ଡିସେମ୍ବର୍", + ] + month_abbreviations = [ + "", + "ଜାନୁ", + "ଫେବୃ", + "ମାର୍ଚ୍ଚ୍", + "ଅପ୍ରେ", + "ମଇ", + "ଜୁନ୍", + "ଜୁଲା", + "ଅଗ", + "ସେପ୍ଟେ", + "ଅକ୍ଟୋ", + "ନଭେ", + "ଡିସେ", + ] + + day_names = [ + "", + "ସୋମବାର", + "ମଙ୍ଗଳବାର", + "ବୁଧବାର", + "ଗୁରୁବାର", + "ଶୁକ୍ରବାର", + "ଶନିବାର", + "ରବିବାର", + ] + day_abbreviations = [ + "", + "ସୋମ", + "ମଙ୍ଗଳ", + "ବୁଧ", + "ଗୁରୁ", + "ଶୁକ୍ର", + "ଶନି", + "ରବି", + ] + + def _ordinal_number(self, n: int) -> str: + if n > 10 or n == 0: + return f"{n}ତମ" + if n in [1, 5, 7, 8, 9, 10]: + return f"{n}ମ" + if n in [2, 3]: + return f"{n}ୟ" + if n == 4: + return f"{n}ର୍ଥ" + if n == 6: + return f"{n}ଷ୍ଠ" + return "" + + +class SerbianLocale(Locale): + names = ["sr", "sr-rs", "sr-sp"] + + past = "pre {0}" + future = "za {0}" + and_word = "i" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = { + "now": "sada", + "second": "sekundu", + "seconds": {"double": "{0} sekunde", "higher": "{0} sekundi"}, + "minute": "minutu", + "minutes": {"double": "{0} minute", "higher": "{0} minuta"}, + "hour": "sat", + "hours": {"double": "{0} sata", "higher": "{0} sati"}, + "day": "dan", + "days": {"double": "{0} dana", "higher": "{0} dana"}, + "week": "nedelju", + "weeks": {"double": "{0} nedelje", "higher": "{0} nedelja"}, + "month": "mesec", + "months": {"double": "{0} meseca", "higher": "{0} meseci"}, + "year": "godinu", + "years": {"double": "{0} godine", "higher": "{0} godina"}, + } + + month_names = [ + "", + "januar", # јануар + "februar", # фебруар + "mart", # март + "april", # април + "maj", # мај + "jun", # јун + "jul", # јул + "avgust", # август + "septembar", # септембар + "oktobar", # октобар + "novembar", # новембар + "decembar", # децембар + ] + + month_abbreviations = [ + "", + "jan", + "feb", + "mar", + "apr", + "maj", + "jun", + "jul", + "avg", + "sep", + "okt", + "nov", + "dec", + ] + + day_names = [ + "", + "ponedeljak", # понедељак + "utorak", # уторак + "sreda", # среда + "četvrtak", # четвртак + "petak", # петак + "subota", # субота + "nedelja", # недеља + ] + + day_abbreviations = [ + "", + "po", # по + "ut", # ут + "sr", # ср + "če", # че + "pe", # пе + "su", # су + "ne", # не + ] + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + form = self.timeframes[timeframe] + delta = abs(delta) + if isinstance(form, Mapping): + if 1 < delta <= 4: + form = form["double"] + else: + form = form["higher"] + + return form.format(delta) + + +class LuxembourgishLocale(Locale): + names = ["lb", "lb-lu"] + + past = "virun {0}" + future = "an {0}" + and_word = "an" + + timeframes = { + "now": "just elo", + "second": "enger Sekonn", + "seconds": "{0} Sekonnen", + "minute": "enger Minutt", + "minutes": "{0} Minutten", + "hour": "enger Stonn", + "hours": "{0} Stonnen", + "day": "engem Dag", + "days": "{0} Deeg", + "week": "enger Woch", + "weeks": "{0} Wochen", + "month": "engem Mount", + "months": "{0} Méint", + "year": "engem Joer", + "years": "{0} Jahren", + } + + timeframes_only_distance = timeframes.copy() + timeframes_only_distance["second"] = "eng Sekonn" + timeframes_only_distance["minute"] = "eng Minutt" + timeframes_only_distance["hour"] = "eng Stonn" + timeframes_only_distance["day"] = "een Dag" + timeframes_only_distance["days"] = "{0} Deeg" + timeframes_only_distance["week"] = "eng Woch" + timeframes_only_distance["month"] = "ee Mount" + timeframes_only_distance["months"] = "{0} Méint" + timeframes_only_distance["year"] = "ee Joer" + timeframes_only_distance["years"] = "{0} Joer" + + month_names = [ + "", + "Januar", + "Februar", + "Mäerz", + "Abrëll", + "Mee", + "Juni", + "Juli", + "August", + "September", + "Oktouber", + "November", + "Dezember", + ] + + month_abbreviations = [ + "", + "Jan", + "Feb", + "Mäe", + "Abr", + "Mee", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez", + ] + + day_names = [ + "", + "Méindeg", + "Dënschdeg", + "Mëttwoch", + "Donneschdeg", + "Freideg", + "Samschdeg", + "Sonndeg", + ] + + day_abbreviations = ["", "Méi", "Dën", "Mët", "Don", "Fre", "Sam", "Son"] + + def _ordinal_number(self, n: int) -> str: + return f"{n}." + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[int, float] = 0, + only_distance: bool = False, + ) -> str: + if not only_distance: + return super().describe(timeframe, delta, only_distance) + + # Luxembourgish uses a different case without 'in' or 'ago' + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized + + +class ZuluLocale(Locale): + names = ["zu", "zu-za"] + + past = "{0} edlule" + future = "{0} " + and_word = "futhi" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "manje", + "second": {"past": "umzuzwana", "future": "ngomzuzwana"}, + "seconds": {"past": "{0} imizuzwana", "future": "{0} ngemizuzwana"}, + "minute": {"past": "umzuzu", "future": "ngomzuzu"}, + "minutes": {"past": "{0} imizuzu", "future": "{0} ngemizuzu"}, + "hour": {"past": "ihora", "future": "ngehora"}, + "hours": {"past": "{0} amahora", "future": "{0} emahoreni"}, + "day": {"past": "usuku", "future": "ngosuku"}, + "days": {"past": "{0} izinsuku", "future": "{0} ezinsukwini"}, + "week": {"past": "isonto", "future": "ngesonto"}, + "weeks": {"past": "{0} amasonto", "future": "{0} emasontweni"}, + "month": {"past": "inyanga", "future": "ngenyanga"}, + "months": {"past": "{0} izinyanga", "future": "{0} ezinyangeni"}, + "year": {"past": "unyaka", "future": "ngonyak"}, + "years": {"past": "{0} iminyaka", "future": "{0} eminyakeni"}, + } + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """Zulu aware time frame format function, takes into account + the differences between past and future forms.""" + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + month_names = [ + "", + "uMasingane", + "uNhlolanja", + "uNdasa", + "UMbasa", + "UNhlaba", + "UNhlangulana", + "uNtulikazi", + "UNcwaba", + "uMandulo", + "uMfumfu", + "uLwezi", + "uZibandlela", + ] + + month_abbreviations = [ + "", + "uMasingane", + "uNhlolanja", + "uNdasa", + "UMbasa", + "UNhlaba", + "UNhlangulana", + "uNtulikazi", + "UNcwaba", + "uMandulo", + "uMfumfu", + "uLwezi", + "uZibandlela", + ] + + day_names = [ + "", + "uMsombuluko", + "uLwesibili", + "uLwesithathu", + "uLwesine", + "uLwesihlanu", + "uMgqibelo", + "iSonto", + ] + + day_abbreviations = [ + "", + "uMsombuluko", + "uLwesibili", + "uLwesithathu", + "uLwesine", + "uLwesihlanu", + "uMgqibelo", + "iSonto", + ] + + +class TamilLocale(Locale): + names = ["ta", "ta-in", "ta-lk"] + + past = "{0} நேரத்திற்கு முன்பு" + future = "இல் {0}" + + timeframes = { + "now": "இப்போது", + "second": "ஒரு இரண்டாவது", + "seconds": "{0} விநாடிகள்", + "minute": "ஒரு நிமிடம்", + "minutes": "{0} நிமிடங்கள்", + "hour": "ஒரு மணி", + "hours": "{0} மணிநேரம்", + "day": "ஒரு நாள்", + "days": "{0} நாட்கள்", + "week": "ஒரு வாரம்", + "weeks": "{0} வாரங்கள்", + "month": "ஒரு மாதம்", + "months": "{0} மாதங்கள்", + "year": "ஒரு ஆண்டு", + "years": "{0} ஆண்டுகள்", + } + + month_names = [ + "", + "சித்திரை", + "வைகாசி", + "ஆனி", + "ஆடி", + "ஆவணி", + "புரட்டாசி", + "ஐப்பசி", + "கார்த்திகை", + "மார்கழி", + "தை", + "மாசி", + "பங்குனி", + ] + + month_abbreviations = [ + "", + "ஜன", + "பிப்", + "மார்", + "ஏப்", + "மே", + "ஜூன்", + "ஜூலை", + "ஆக", + "செப்", + "அக்", + "நவ", + "டிச", + ] + + day_names = [ + "", + "திங்கட்கிழமை", + "செவ்வாய்க்கிழமை", + "புதன்கிழமை", + "வியாழக்கிழமை", + "வெள்ளிக்கிழமை", + "சனிக்கிழமை", + "ஞாயிற்றுக்கிழமை", + ] + + day_abbreviations = [ + "", + "திங்கட்", + "செவ்வாய்", + "புதன்", + "வியாழன்", + "வெள்ளி", + "சனி", + "ஞாயிறு", + ] + + def _ordinal_number(self, n: int) -> str: + if n == 1: + return f"{n}வது" + elif n >= 0: + return f"{n}ஆம்" + else: + return "" + + +class AlbanianLocale(Locale): + names = ["sq", "sq-al"] + + past = "{0} më parë" + future = "në {0}" + and_word = "dhe" + + timeframes = { + "now": "tani", + "second": "sekondë", + "seconds": "{0} sekonda", + "minute": "minutë", + "minutes": "{0} minuta", + "hour": "orë", + "hours": "{0} orë", + "day": "ditë", + "days": "{0} ditë", + "week": "javë", + "weeks": "{0} javë", + "month": "muaj", + "months": "{0} muaj", + "year": "vit", + "years": "{0} vjet", + } + + month_names = [ + "", + "janar", + "shkurt", + "mars", + "prill", + "maj", + "qershor", + "korrik", + "gusht", + "shtator", + "tetor", + "nëntor", + "dhjetor", + ] + + month_abbreviations = [ + "", + "jan", + "shk", + "mar", + "pri", + "maj", + "qer", + "korr", + "gush", + "sht", + "tet", + "nën", + "dhj", + ] + + day_names = [ + "", + "e hënë", + "e martë", + "e mërkurë", + "e enjte", + "e premte", + "e shtunë", + "e diel", + ] + + day_abbreviations = [ + "", + "hën", + "mar", + "mër", + "enj", + "pre", + "sht", + "die", + ] + + +class GeorgianLocale(Locale): + names = ["ka", "ka-ge"] + + past = "{0} წინ" # ts’in + future = "{0} შემდეგ" # shemdeg + and_word = "და" # da + + timeframes = { + "now": "ახლა", # akhla + # When a cardinal qualifies a noun, it stands in the singular + "second": "წამის", # ts’amis + "seconds": "{0} წამის", + "minute": "წუთის", # ts’utis + "minutes": "{0} წუთის", + "hour": "საათის", # saatis + "hours": "{0} საათის", + "day": "დღის", # dghis + "days": "{0} დღის", + "week": "კვირის", # k’viris + "weeks": "{0} კვირის", + "month": "თვის", # tvis + "months": "{0} თვის", + "year": "წლის", # ts’lis + "years": "{0} წლის", + } + + month_names = [ + # modern month names + "", + "იანვარი", # Ianvari + "თებერვალი", # Tebervali + "მარტი", # Mart'i + "აპრილი", # Ap'rili + "მაისი", # Maisi + "ივნისი", # Ivnisi + "ივლისი", # Ivlisi + "აგვისტო", # Agvist'o + "სექტემბერი", # Sekt'emberi + "ოქტომბერი", # Okt'omberi + "ნოემბერი", # Noemberi + "დეკემბერი", # Dek'emberi + ] + + month_abbreviations = [ + # no abbr. found yet + "", + "იანვარი", # Ianvari + "თებერვალი", # Tebervali + "მარტი", # Mart'i + "აპრილი", # Ap'rili + "მაისი", # Maisi + "ივნისი", # Ivnisi + "ივლისი", # Ivlisi + "აგვისტო", # Agvist'o + "სექტემბერი", # Sekt'emberi + "ოქტომბერი", # Okt'omberi + "ნოემბერი", # Noemberi + "დეკემბერი", # Dek'emberi + ] + + day_names = [ + "", + "ორშაბათი", # orshabati + "სამშაბათი", # samshabati + "ოთხშაბათი", # otkhshabati + "ხუთშაბათი", # khutshabati + "პარასკევი", # p’arask’evi + "შაბათი", # shabati + # "k’vira" also serves as week; to avoid confusion "k’vira-dge" can be used for Sunday + "კვირა", # k’vira + ] + + day_abbreviations = [ + "", + "ორშაბათი", # orshabati + "სამშაბათი", # samshabati + "ოთხშაბათი", # otkhshabati + "ხუთშაბათი", # khutshabati + "პარასკევი", # p’arask’evi + "შაბათი", # shabati + "კვირა", # k’vira + ] + + +class SinhalaLocale(Locale): + names = ["si", "si-lk"] + + past = "{0}ට පෙර" + future = "{0}" + and_word = "සහ" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "දැන්", + "second": { + "past": "තත්පරයක", + "future": "තත්පරයකින්", + }, # ක් is the article + "seconds": { + "past": "තත්පර {0} ක", + "future": "තත්පර {0} කින්", + }, + "minute": { + "past": "විනාඩියක", + "future": "විනාඩියකින්", + }, + "minutes": { + "past": "විනාඩි {0} ක", + "future": "මිනිත්තු {0} කින්", + }, + "hour": {"past": "පැයක", "future": "පැයකින්"}, + "hours": { + "past": "පැය {0} ක", + "future": "පැය {0} කින්", + }, + "day": {"past": "දිනක", "future": "දිනකට"}, + "days": { + "past": "දින {0} ක", + "future": "දින {0} කින්", + }, + "week": {"past": "සතියක", "future": "සතියකින්"}, + "weeks": { + "past": "සති {0} ක", + "future": "සති {0} කින්", + }, + "month": {"past": "මාසයක", "future": "එය මාසය තුළ"}, + "months": { + "past": "මාස {0} ක", + "future": "මාස {0} කින්", + }, + "year": {"past": "වසරක", "future": "වසරක් තුළ"}, + "years": { + "past": "අවුරුදු {0} ක", + "future": "අවුරුදු {0} තුළ", + }, + } + # Sinhala: the general format to describe timeframe is different from past and future, + # so we do not copy the original timeframes dictionary + timeframes_only_distance = {} + timeframes_only_distance["second"] = "තත්පරයක්" + timeframes_only_distance["seconds"] = "තත්පර {0}" + timeframes_only_distance["minute"] = "මිනිත්තුවක්" + timeframes_only_distance["minutes"] = "විනාඩි {0}" + timeframes_only_distance["hour"] = "පැයක්" + timeframes_only_distance["hours"] = "පැය {0}" + timeframes_only_distance["day"] = "දවසක්" + timeframes_only_distance["days"] = "දවස් {0}" + timeframes_only_distance["week"] = "සතියක්" + timeframes_only_distance["weeks"] = "සති {0}" + timeframes_only_distance["month"] = "මාසයක්" + timeframes_only_distance["months"] = "මාස {0}" + timeframes_only_distance["year"] = "අවුරුද්දක්" + timeframes_only_distance["years"] = "අවුරුදු {0}" + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """ + Sinhala awares time frame format function, takes into account + the differences between general, past, and future forms (three different suffixes). + """ + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[float, int] = 1, # key is always future when only_distance=False + only_distance: bool = False, + ) -> str: + """Describes a delta within a timeframe in plain language. + + :param timeframe: a string representing a timeframe. + :param delta: a quantity representing a delta in a timeframe. + :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords + """ + + if not only_distance: + return super().describe(timeframe, delta, only_distance) + # Sinhala uses a different case without 'in' or 'ago' + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized + + month_names = [ + "", + "ජනවාරි", + "පෙබරවාරි", + "මාර්තු", + "අප්‍රේල්", + "මැයි", + "ජූනි", + "ජූලි", + "අගෝස්තු", + "සැප්තැම්බර්", + "ඔක්තෝබර්", + "නොවැම්බර්", + "දෙසැම්බර්", + ] + + month_abbreviations = [ + "", + "ජන", + "පෙබ", + "මාර්", + "අප්‍රේ", + "මැයි", + "ජුනි", + "ජූලි", + "අගෝ", + "සැප්", + "ඔක්", + "නොවැ", + "දෙසැ", + ] + + day_names = [ + "", + "සදුදා", + "අඟහරැවදා", + "බදාදා", + "බ්‍රහස්‍පතින්‍දා", + "සිකුරාදා", + "සෙනසුරාදා", + "ඉරිදා", + ] + + day_abbreviations = [ + "", + "සදුද", + "බදා", + "බදා", + "සිකු", + "සෙන", + "අ", + "ඉරිදා", + ] + + +class UrduLocale(Locale): + names = ["ur", "ur-pk"] + + past = "پہلے {0}" + future = "میں {0}" + and_word = "اور" + + timeframes = { + "now": "ابھی", + "second": "ایک سیکنڈ", + "seconds": "{0} سیکنڈ", + "minute": "ایک منٹ", + "minutes": "{0} منٹ", + "hour": "ایک گھنٹے", + "hours": "{0} گھنٹے", + "day": "ایک دن", + "days": "{0} دن", + "week": "ایک ہفتے", + "weeks": "{0} ہفتے", + "month": "ایک مہینہ", + "months": "{0} ماہ", + "year": "ایک سال", + "years": "{0} سال", + } + + month_names = [ + "", + "جنوری", + "فروری", + "مارچ", + "اپریل", + "مئی", + "جون", + "جولائی", + "اگست", + "ستمبر", + "اکتوبر", + "نومبر", + "دسمبر", + ] + + month_abbreviations = [ + "", + "جنوری", + "فروری", + "مارچ", + "اپریل", + "مئی", + "جون", + "جولائی", + "اگست", + "ستمبر", + "اکتوبر", + "نومبر", + "دسمبر", + ] + + day_names = [ + "", + "سوموار", + "منگل", + "بدھ", + "جمعرات", + "جمعہ", + "ہفتہ", + "اتوار", + ] + + day_abbreviations = [ + "", + "سوموار", + "منگل", + "بدھ", + "جمعرات", + "جمعہ", + "ہفتہ", + "اتوار", + ] + + +class KazakhLocale(Locale): + names = ["kk", "kk-kz"] + + past = "{0} бұрын" + future = "{0} кейін" + timeframes = { + "now": "қазір", + "second": "бір секунд", + "seconds": "{0} секунд", + "minute": "бір минут", + "minutes": "{0} минут", + "hour": "бір сағат", + "hours": "{0} сағат", + "day": "бір күн", + "days": "{0} күн", + "week": "бір апта", + "weeks": "{0} апта", + "month": "бір ай", + "months": "{0} ай", + "year": "бір жыл", + "years": "{0} жыл", + } + + month_names = [ + "", + "Қаңтар", + "Ақпан", + "Наурыз", + "Сәуір", + "Мамыр", + "Маусым", + "Шілде", + "Тамыз", + "Қыркүйек", + "Қазан", + "Қараша", + "Желтоқсан", + ] + month_abbreviations = [ + "", + "Қан", + "Ақп", + "Нау", + "Сәу", + "Мам", + "Мау", + "Шіл", + "Там", + "Қыр", + "Қаз", + "Қар", + "Жел", + ] + + day_names = [ + "", + "Дүйсембі", + "Сейсенбі", + "Сәрсенбі", + "Бейсенбі", + "Жұма", + "Сенбі", + "Жексенбі", + ] + day_abbreviations = ["", "Дс", "Сс", "Ср", "Бс", "Жм", "Сб", "Жс"] + + +class AmharicLocale(Locale): + names = ["am", "am-et"] + + past = "{0} በፊት" + future = "{0} ውስጥ" + and_word = "እና" + + timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[Mapping[str, str], str]]] = { + "now": "አሁን", + "second": { + "past": "ከአንድ ሰከንድ", + "future": "በአንድ ሰከንድ", + }, + "seconds": { + "past": "ከ {0} ሰከንድ", + "future": "በ {0} ሰከንድ", + }, + "minute": { + "past": "ከአንድ ደቂቃ", + "future": "በአንድ ደቂቃ", + }, + "minutes": { + "past": "ከ {0} ደቂቃዎች", + "future": "በ {0} ደቂቃዎች", + }, + "hour": { + "past": "ከአንድ ሰዓት", + "future": "በአንድ ሰዓት", + }, + "hours": { + "past": "ከ {0} ሰዓታት", + "future": "በ {0} ሰከንድ", + }, + "day": { + "past": "ከአንድ ቀን", + "future": "በአንድ ቀን", + }, + "days": { + "past": "ከ {0} ቀናት", + "future": "በ {0} ቀናት", + }, + "week": { + "past": "ከአንድ ሳምንት", + "future": "በአንድ ሳምንት", + }, + "weeks": { + "past": "ከ {0} ሳምንታት", + "future": "በ {0} ሳምንታት", + }, + "month": { + "past": "ከአንድ ወር", + "future": "በአንድ ወር", + }, + "months": { + "past": "ከ {0} ወር", + "future": "በ {0} ወራት", + }, + "year": { + "past": "ከአንድ አመት", + "future": "በአንድ አመት", + }, + "years": { + "past": "ከ {0} ዓመታት", + "future": "በ {0} ዓመታት", + }, + } + # Amharic: the general format to describe timeframe is different from past and future, + # so we do not copy the original timeframes dictionary + timeframes_only_distance = { + "second": "አንድ ሰከንድ", + "seconds": "{0} ሰከንድ", + "minute": "አንድ ደቂቃ", + "minutes": "{0} ደቂቃዎች", + "hour": "አንድ ሰዓት", + "hours": "{0} ሰዓት", + "day": "አንድ ቀን", + "days": "{0} ቀናት", + "week": "አንድ ሳምንት", + "weeks": "{0} ሳምንት", + "month": "አንድ ወር", + "months": "{0} ወራት", + "year": "አንድ አመት", + "years": "{0} ዓመታት", + } + + month_names = [ + "", + "ጃንዩወሪ", + "ፌብሩወሪ", + "ማርች", + "ኤፕሪል", + "ሜይ", + "ጁን", + "ጁላይ", + "ኦገስት", + "ሴፕቴምበር", + "ኦክቶበር", + "ኖቬምበር", + "ዲሴምበር", + ] + + month_abbreviations = [ + "", + "ጃንዩ", + "ፌብሩ", + "ማርች", + "ኤፕሪ", + "ሜይ", + "ጁን", + "ጁላይ", + "ኦገስ", + "ሴፕቴ", + "ኦክቶ", + "ኖቬም", + "ዲሴም", + ] + + day_names = [ + "", + "ሰኞ", + "ማክሰኞ", + "ረቡዕ", + "ሐሙስ", + "ዓርብ", + "ቅዳሜ", + "እሑድ", + ] + day_abbreviations = ["", "እ", "ሰ", "ማ", "ረ", "ሐ", "ዓ", "ቅ"] + + def _ordinal_number(self, n: int) -> str: + return f"{n}ኛ" + + def _format_timeframe(self, timeframe: TimeFrameLiteral, delta: int) -> str: + """ + Amharic awares time frame format function, takes into account + the differences between general, past, and future forms (three different suffixes). + """ + abs_delta = abs(delta) + form = self.timeframes[timeframe] + + if isinstance(form, str): + return form.format(abs_delta) + + if delta > 0: + key = "future" + else: + key = "past" + form = form[key] + + return form.format(abs_delta) + + def describe( + self, + timeframe: TimeFrameLiteral, + delta: Union[float, int] = 1, # key is always future when only_distance=False + only_distance: bool = False, + ) -> str: + """Describes a delta within a timeframe in plain language. + + :param timeframe: a string representing a timeframe. + :param delta: a quantity representing a delta in a timeframe. + :param only_distance: return only distance eg: "11 seconds" without "in" or "ago" keywords + """ + + if not only_distance: + return super().describe(timeframe, delta, only_distance) + humanized = self.timeframes_only_distance[timeframe].format(trunc(abs(delta))) + + return humanized + + +class ArmenianLocale(Locale): + names = ["hy", "hy-am"] + past = "{0} առաջ" + future = "{0}ից" + and_word = "Եվ" # Yev + + timeframes = { + "now": "հիմա", + "second": "վայրկյան", + "seconds": "{0} վայրկյան", + "minute": "րոպե", + "minutes": "{0} րոպե", + "hour": "ժամ", + "hours": "{0} ժամ", + "day": "օր", + "days": "{0} օր", + "month": "ամիս", + "months": "{0} ամիս", + "year": "տարին", + "years": "{0} տարին", + "week": "շաբաթ", + "weeks": "{0} շաբաթ", + } + + meridians = { + "am": "Ամ", + "pm": "պ.մ.", + "AM": "Ամ", + "PM": "պ.մ.", + } + + month_names = [ + "", + "հունվար", + "փետրվար", + "մարտ", + "ապրիլ", + "մայիս", + "հունիս", + "հուլիս", + "օգոստոս", + "սեպտեմբեր", + "հոկտեմբեր", + "նոյեմբեր", + "դեկտեմբեր", + ] + + month_abbreviations = [ + "", + "հունվար", + "փետրվար", + "մարտ", + "ապրիլ", + "մայիս", + "հունիս", + "հուլիս", + "օգոստոս", + "սեպտեմբեր", + "հոկտեմբեր", + "նոյեմբեր", + "դեկտեմբեր", + ] + + day_names = [ + "", + "երկուշաբթի", + "երեքշաբթի", + "չորեքշաբթի", + "հինգշաբթի", + "ուրբաթ", + "շաբաթ", + "կիրակի", + ] + + day_abbreviations = [ + "", + "երկ.", + "երեք.", + "չորեք.", + "հինգ.", + "ուրբ.", + "շաբ.", + "կիր.", + ] + + +class UzbekLocale(Locale): + names = ["uz", "uz-uz"] + past = "{0}dan avval" + future = "{0}dan keyin" + timeframes = { + "now": "hozir", + "second": "bir soniya", + "seconds": "{0} soniya", + "minute": "bir daqiqa", + "minutes": "{0} daqiqa", + "hour": "bir soat", + "hours": "{0} soat", + "day": "bir kun", + "days": "{0} kun", + "week": "bir hafta", + "weeks": "{0} hafta", + "month": "bir oy", + "months": "{0} oy", + "year": "bir yil", + "years": "{0} yil", + } + + month_names = [ + "", + "Yanvar", + "Fevral", + "Mart", + "Aprel", + "May", + "Iyun", + "Iyul", + "Avgust", + "Sentyabr", + "Oktyabr", + "Noyabr", + "Dekabr", + ] + + month_abbreviations = [ + "", + "Yan", + "Fev", + "Mar", + "Apr", + "May", + "Iyn", + "Iyl", + "Avg", + "Sen", + "Okt", + "Noy", + "Dek", + ] + + day_names = [ + "", + "Dushanba", + "Seshanba", + "Chorshanba", + "Payshanba", + "Juma", + "Shanba", + "Yakshanba", + ] + + day_abbreviations = ["", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan", "Yak"] diff --git a/script.module.arrow/lib/arrow/parser.py b/script.module.arrow/lib/arrow/parser.py index a6a617dbc..e95d78b0d 100644 --- a/script.module.arrow/lib/arrow/parser.py +++ b/script.module.arrow/lib/arrow/parser.py @@ -1,3 +1,5 @@ +"""Provides the :class:`Arrow ` class, a better way to parse datetime strings.""" + import re import sys from datetime import datetime, timedelta @@ -23,6 +25,7 @@ from dateutil import tz from arrow import locales +from arrow.constants import DEFAULT_LOCALE from arrow.util import next_weekday, normalize_timestamp if sys.version_info < (3, 8): # pragma: no cover @@ -155,7 +158,7 @@ class DateTimeParser: locale: locales.Locale _input_re_map: Dict[_FORMAT_TYPE, Pattern[str]] - def __init__(self, locale: str = "en_us", cache_size: int = 0) -> None: + def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None: self.locale = locales.get_locale(locale) self._input_re_map = self._BASE_INPUT_RE_MAP.copy() @@ -571,9 +574,12 @@ def _parse_token( elif token in ["a", "A"]: if value in (self.locale.meridians["am"], self.locale.meridians["AM"]): parts["am_pm"] = "am" + if "hour" in parts and not 0 <= parts["hour"] <= 12: + raise ParserMatchError( + f"Hour token value must be between 0 and 12 inclusive for token {token!r}." + ) elif value in (self.locale.meridians["pm"], self.locale.meridians["PM"]): parts["am_pm"] = "pm" - elif token == "W": parts["weekdate"] = value diff --git a/script.module.arrow/lib/arrow/util.py b/script.module.arrow/lib/arrow/util.py index 8679131ee..f3eaa21c9 100644 --- a/script.module.arrow/lib/arrow/util.py +++ b/script.module.arrow/lib/arrow/util.py @@ -1,3 +1,5 @@ +"""Helpful functions used internally within arrow.""" + import datetime from typing import Any, Optional, cast @@ -57,7 +59,11 @@ def is_timestamp(value: Any) -> bool: def validate_ordinal(value: Any) -> None: - """Raise the corresponding exception if value is an invalid Gregorian ordinal.""" + """Raise an exception if value is an invalid Gregorian ordinal. + + :param value: the input to be checked + + """ if isinstance(value, bool) or not isinstance(value, int): raise TypeError(f"Ordinal must be an integer (got type {type(value)}).") if not (MIN_ORDINAL <= value <= MAX_ORDINAL): @@ -78,7 +84,13 @@ def normalize_timestamp(timestamp: float) -> float: # Credit to https://stackoverflow.com/a/1700069 def iso_to_gregorian(iso_year: int, iso_week: int, iso_day: int) -> datetime.date: - """Converts an ISO week date tuple into a datetime object.""" + """Converts an ISO week date into a datetime object. + + :param iso_year: the year + :param iso_week: the week number, each year has either 52 or 53 weeks + :param iso_day: the day numbered 1 through 7, beginning with Monday + + """ if not 1 <= iso_week <= 53: raise ValueError("ISO Calendar week value must be between 1-53.") diff --git a/script.module.arrow/icon.png b/script.module.arrow/resources/icon.png similarity index 100% rename from script.module.arrow/icon.png rename to script.module.arrow/resources/icon.png diff --git a/script.module.beautifulsoup4/AUTHORS.txt b/script.module.beautifulsoup4/AUTHORS.txt deleted file mode 100644 index 1f14fe07d..000000000 --- a/script.module.beautifulsoup4/AUTHORS.txt +++ /dev/null @@ -1,49 +0,0 @@ -Behold, mortal, the origins of Beautiful Soup... -================================================ - -Leonard Richardson is the primary maintainer. - -Aaron DeVore and Isaac Muse have made significant contributions to the -code base. - -Mark Pilgrim provided the encoding detection code that forms the base -of UnicodeDammit. - -Thomas Kluyver and Ezio Melotti finished the work of getting Beautiful -Soup 4 working under Python 3. - -Simon Willison wrote soupselect, which was used to make Beautiful Soup -support CSS selectors. Isaac Muse wrote SoupSieve, which made it -possible to _remove_ the CSS selector code from Beautiful Soup. - -Sam Ruby helped with a lot of edge cases. - -Jonathan Ellis was awarded the prestigious Beau Potage D'Or for his -work in solving the nestable tags conundrum. - -An incomplete list of people have contributed patches to Beautiful -Soup: - - Istvan Albert, Andrew Lin, Anthony Baxter, Oliver Beattie, Andrew -Boyko, Tony Chang, Francisco Canas, "Delong", Zephyr Fang, Fuzzy, -Roman Gaufman, Yoni Gilad, Richie Hindle, Toshihiro Kamiya, Peteris -Krumins, Kent Johnson, Marek Kapolka, Andreas Kostyrka, Roel Kramer, -Ben Last, Robert Leftwich, Stefaan Lippens, "liquider", Staffan -Malmgren, Ksenia Marasanova, JP Moins, Adam Monsen, John Nagle, "Jon", -Ed Oskiewicz, Martijn Peters, Greg Phillips, Giles Radford, Stefano -Revera, Arthur Rudolph, Marko Samastur, James Salter, Jouni Seppnen, -Alexander Schmolck, Tim Shirley, Geoffrey Sneddon, Ville Skytt, -"Vikas", Jens Svalgaard, Andy Theyers, Eric Weiser, Glyn Webster, John -Wiseman, Paul Wright, Danny Yoo - -An incomplete list of people who made suggestions or found bugs or -found ways to break Beautiful Soup: - - Hanno Bck, Matteo Bertini, Chris Curvey, Simon Cusack, Bruce Eckel, - Matt Ernst, Michael Foord, Tom Harris, Bill de hOra, Donald Howes, - Matt Patterson, Scott Roberts, Steve Strassmann, Mike Williams, - warchild at redho dot com, Sami Kuisma, Carlos Rocha, Bob Hutchison, - Joren Mc, Michal Migurski, John Kleven, Tim Heaney, Tripp Lilley, Ed - Summers, Dennis Sutch, Chris Smith, Aaron Swartz, Stuart - Turner, Greg Edwards, Kevin J Kalupson, Nikos Kouremenos, Artur de - Sousa Rocha, Yichun Wei, Per Vognsen diff --git a/script.module.beautifulsoup4/COPYING.txt b/script.module.beautifulsoup4/COPYING.txt deleted file mode 100644 index fb6ae69cd..000000000 --- a/script.module.beautifulsoup4/COPYING.txt +++ /dev/null @@ -1,27 +0,0 @@ -Beautiful Soup is made available under the MIT license: - - Copyright (c) 2004-2017 Leonard Richardson - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - -Beautiful Soup incorporates code from the html5lib library, which is -also made available under the MIT license. Copyright (c) 2006-2013 -James Graham and other contributors diff --git a/script.module.beautifulsoup4/LICENSE b/script.module.beautifulsoup4/LICENSE deleted file mode 100644 index 4c068bab2..000000000 --- a/script.module.beautifulsoup4/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Beautiful Soup is made available under the MIT license: - - Copyright (c) 2004-2019 Leonard Richardson - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - -Beautiful Soup incorporates code from the html5lib library, which is -also made available under the MIT license. Copyright (c) 2006-2013 -James Graham and other contributors - -Beautiful Soup depends on the soupsieve library, which is also made -available under the MIT license. Copyright (c) 2018 Isaac Muse diff --git a/script.module.beautifulsoup4/LICENSE.txt b/script.module.beautifulsoup4/LICENSE.txt new file mode 100644 index 000000000..08e3a9cf8 --- /dev/null +++ b/script.module.beautifulsoup4/LICENSE.txt @@ -0,0 +1,31 @@ +Beautiful Soup is made available under the MIT license: + + Copyright (c) Leonard Richardson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Beautiful Soup incorporates code from the html5lib library, which is +also made available under the MIT license. Copyright (c) James Graham +and other contributors + +Beautiful Soup has an optional dependency on the soupsieve library, +which is also made available under the MIT license. Copyright (c) +Isaac Muse diff --git a/script.module.beautifulsoup4/NEWS.txt b/script.module.beautifulsoup4/NEWS.txt deleted file mode 100644 index 2758e620f..000000000 --- a/script.module.beautifulsoup4/NEWS.txt +++ /dev/null @@ -1,1475 +0,0 @@ -= 4.8.2 (20191224) - -* Added Python docstrings to all public methods of the most commonly - used classes. - -* Added a Chinese translation by Deron Wang and a Brazilian Portuguese - translation by Cezar Peixeiro to the repository. - -* Fixed two deprecation warnings. Patches by Colin - Watson and Nicholas Neumann. [bug=1847592] [bug=1855301] - -* The html.parser tree builder now correctly handles DOCTYPEs that are - not uppercase. [bug=1848401] - -* PageElement.select() now returns a ResultSet rather than a regular - list, making it consistent with methods like find_all(). - -= 4.8.1 (20191006) - -* When the html.parser or html5lib parsers are in use, Beautiful Soup - will, by default, record the position in the original document where - each tag was encountered. This includes line number (Tag.sourceline) - and position within a line (Tag.sourcepos). Based on code by Chris - Mayo. [bug=1742921] - -* When instantiating a BeautifulSoup object, it's now possible to - provide a dictionary ('element_classes') of the classes you'd like to be - instantiated instead of Tag, NavigableString, etc. - -* Fixed the definition of the default XML namespace when using - lxml 4.4. Patch by Isaac Muse. [bug=1840141] - -* Fixed a crash when pretty-printing tags that were not created - during initial parsing. [bug=1838903] - -* Copying a Tag preserves information that was originally obtained from - the TreeBuilder used to build the original Tag. [bug=1838903] - -* Raise an explanatory exception when the underlying parser - completely rejects the incoming markup. [bug=1838877] - -* Avoid a crash when trying to detect the declared encoding of a - Unicode document. [bug=1838877] - -* Avoid a crash when unpickling certain parse trees generated - using html5lib on Python 3. [bug=1843545] - -= 4.8.0 (20190720, "One Small Soup") - -This release focuses on making it easier to customize Beautiful Soup's -input mechanism (the TreeBuilder) and output mechanism (the Formatter). - -* You can customize the TreeBuilder object by passing keyword - arguments into the BeautifulSoup constructor. Those keyword - arguments will be passed along into the TreeBuilder constructor. - - The main reason to do this right now is to change how which - attributes are treated as multi-valued attributes (the way 'class' - is treated by default). You can do this with the - 'multi_valued_attributes' argument. [bug=1832978] - -* The role of Formatter objects has been greatly expanded. The Formatter - class now controls the following: - - - The function to call to perform entity substitution. (This was - previously Formatter's only job.) - - Which tags should be treated as containing CDATA and have their - contents exempt from entity substitution. - - The order in which a tag's attributes are output. [bug=1812422] - - Whether or not to put a '/' inside a void element, e.g. '
' vs '
' - - All preexisting code should work as before. - -* Added a new method to the API, Tag.smooth(), which consolidates - multiple adjacent NavigableString elements. [bug=1697296] - -* ' (which is valid in XML, XHTML, and HTML 5, but not HTML 4) is always - recognized as a named entity and converted to a single quote. [bug=1818721] - -= 4.7.1 (20190106) - -* Fixed a significant performance problem introduced in 4.7.0. [bug=1810617] - -* Fixed an incorrectly raised exception when inserting a tag before or - after an identical tag. [bug=1810692] - -* Beautiful Soup will no longer try to keep track of namespaces that - are not defined with a prefix; this can confuse soupselect. [bug=1810680] - -* Tried even harder to avoid the deprecation warning originally fixed in - 4.6.1. [bug=1778909] - -= 4.7.0 (20181231) - -* Beautiful Soup's CSS Selector implementation has been replaced by a - dependency on Isaac Muse's SoupSieve project (the soupsieve package - on PyPI). The good news is that SoupSieve has a much more robust and - complete implementation of CSS selectors, resolving a large number - of longstanding issues. The bad news is that from this point onward, - SoupSieve must be installed if you want to use the select() method. - - You don't have to change anything lf you installed Beautiful Soup - through pip (SoupSieve will be automatically installed when you - upgrade Beautiful Soup) or if you don't use CSS selectors from - within Beautiful Soup. - - SoupSieve documentation: https://facelessuser.github.io/soupsieve/ - -* Added the PageElement.extend() method, which works like list.append(). - [bug=1514970] - -* PageElement.insert_before() and insert_after() now take a variable - number of arguments. [bug=1514970] - -* Fix a number of problems with the tree builder that caused - trees that were superficially okay, but which fell apart when bits - were extracted. Patch by Isaac Muse. [bug=1782928,1809910] - -* Fixed a problem with the tree builder in which elements that - contained no content (such as empty comments and all-whitespace - elements) were not being treated as part of the tree. Patch by Isaac - Muse. [bug=1798699] - -* Fixed a problem with multi-valued attributes where the value - contained whitespace. Thanks to Jens Svalgaard for the - fix. [bug=1787453] - -* Clarified ambiguous license statements in the source code. Beautiful - Soup is released under the MIT license, and has been since 4.4.0. - -* This file has been renamed from NEWS.txt to CHANGELOG. - -= 4.6.3 (20180812) - -* Exactly the same as 4.6.2. Re-released to make the README file - render properly on PyPI. - -= 4.6.2 (20180812) - -* Fix an exception when a custom formatter was asked to format a void - element. [bug=1784408] - -= 4.6.1 (20180728) - -* Stop data loss when encountering an empty numeric entity, and - possibly in other cases. Thanks to tos.kamiya for the fix. [bug=1698503] - -* Preserve XML namespaces introduced inside an XML document, not just - the ones introduced at the top level. [bug=1718787] - -* Added a new formatter, "html5", which represents void elements - as "" rather than "". [bug=1716272] - -* Fixed a problem where the html.parser tree builder interpreted - a string like "&foo " as the character entity "&foo;" [bug=1728706] - -* Correctly handle invalid HTML numeric character entities like “ - which reference code points that are not Unicode code points. Note - that this is only fixed when Beautiful Soup is used with the - html.parser parser -- html5lib already worked and I couldn't fix it - with lxml. [bug=1782933] - -* Improved the warning given when no parser is specified. [bug=1780571] - -* When markup contains duplicate elements, a select() call that - includes multiple match clauses will match all relevant - elements. [bug=1770596] - -* Fixed code that was causing deprecation warnings in recent Python 3 - versions. Includes a patch from Ville Skyttä. [bug=1778909] [bug=1689496] - -* Fixed a Windows crash in diagnose() when checking whether a long - markup string is a filename. [bug=1737121] - -* Stopped HTMLParser from raising an exception in very rare cases of - bad markup. [bug=1708831] - -* Fixed a bug where find_all() was not working when asked to find a - tag with a namespaced name in an XML document that was parsed as - HTML. [bug=1723783] - -* You can get finer control over formatting by subclassing - bs4.element.Formatter and passing a Formatter instance into (e.g.) - encode(). [bug=1716272] - -* You can pass a dictionary of `attrs` into - BeautifulSoup.new_tag. This makes it possible to create a tag with - an attribute like 'name' that would otherwise be masked by another - argument of new_tag. [bug=1779276] - -* Clarified the deprecation warning when accessing tag.fooTag, to cover - the possibility that you might really have been looking for a tag - called 'fooTag'. - -= 4.6.0 (20170507) = - -* Added the `Tag.get_attribute_list` method, which acts like `Tag.get` for - getting the value of an attribute, but which always returns a list, - whether or not the attribute is a multi-value attribute. [bug=1678589] - -* It's now possible to use a tag's namespace prefix when searching, - e.g. soup.find('namespace:tag') [bug=1655332] - -* Improved the handling of empty-element tags like
when using the - html.parser parser. [bug=1676935] - -* HTML parsers treat all HTML4 and HTML5 empty element tags (aka void - element tags) correctly. [bug=1656909] - -* Namespace prefix is preserved when an XML tag is copied. Thanks - to Vikas for a patch and test. [bug=1685172] - -= 4.5.3 (20170102) = - -* Fixed foster parenting when html5lib is the tree builder. Thanks to - Geoffrey Sneddon for a patch and test. - -* Fixed yet another problem that caused the html5lib tree builder to - create a disconnected parse tree. [bug=1629825] - -= 4.5.2 (20170102) = - -* Apart from the version number, this release is identical to - 4.5.3. Due to user error, it could not be completely uploaded to - PyPI. Use 4.5.3 instead. - -= 4.5.1 (20160802) = - -* Fixed a crash when passing Unicode markup that contained a - processing instruction into the lxml HTML parser on Python - 3. [bug=1608048] - -= 4.5.0 (20160719) = - -* Beautiful Soup is no longer compatible with Python 2.6. This - actually happened a few releases ago, but it's now official. - -* Beautiful Soup will now work with versions of html5lib greater than - 0.99999999. [bug=1603299] - -* If a search against each individual value of a multi-valued - attribute fails, the search will be run one final time against the - complete attribute value considered as a single string. That is, if - a tag has class="foo bar" and neither "foo" nor "bar" matches, but - "foo bar" does, the tag is now considered a match. - - This happened in previous versions, but only when the value being - searched for was a string. Now it also works when that value is - a regular expression, a list of strings, etc. [bug=1476868] - -* Fixed a bug that deranged the tree when a whitespace element was - reparented into a tag that contained an identical whitespace - element. [bug=1505351] - -* Added support for CSS selector values that contain quoted spaces, - such as tag[style="display: foo"]. [bug=1540588] - -* Corrected handling of XML processing instructions. [bug=1504393] - -* Corrected an encoding error that happened when a BeautifulSoup - object was copied. [bug=1554439] - -* The contents of