diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..04ffc0a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: XMLTV-Import + +on: + push: + branches: [ python3 ] + pull_request: + branches: + - "*" + +jobs: + build: + name: Build XMLTV-Import + runs-on: ubuntu-22.04 + strategy: + matrix: + python: ['3.10','3.11','3.12','3.13'] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Build XMLTV-Import, python ${{ matrix.python }} + run: | + python -m compileall . diff --git a/.github/workflows/buildbot.yml b/.github/workflows/buildbot.yml new file mode 100644 index 0000000..9923dfa --- /dev/null +++ b/.github/workflows/buildbot.yml @@ -0,0 +1,28 @@ +name: buildbot + +on: + push: + branches: [ python3 ] + + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + + - uses: actions/checkout@v3 + with: + ref: 'python3' + + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Build python CI + run: | + sudo apt-get -q update + sudo apt-get install dos2unix + pip install --upgrade pip autopep8 + ./CI/build.sh diff --git a/.gitignore b/.gitignore index 0d20b64..80ea821 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.mo \ No newline at end of file diff --git a/CI/PEP8.sh b/CI/PEP8.sh new file mode 100755 index 0000000..24096b5 --- /dev/null +++ b/CI/PEP8.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +echo "" +echo "PEP8 double aggressive safe cleanup by Persian Prince" +# Script by Persian Prince for https://github.com/OpenVisionE2 +# You're not allowed to remove my copyright or reuse this script without putting this header. +echo "" +echo "Changing py files, please wait ..." +begin=$(date +"%s") + +echo "" +echo "PEP8 double aggressive E401" +autopep8 . -a -a -j 0 --recursive --select=E401 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E401" + +echo "" +echo "PEP8 double aggressive E701, E70 and E502" +autopep8 . -a -a -j 0 --recursive --select=E701,E70,E502 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E701, E70 and E502" + +echo "" +echo "PEP8 double aggressive E251 and E252" +autopep8 . -a -a -j 0 --recursive --select=E251,E252 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E251 and E252" + +echo "" +echo "PEP8 double aggressive E20 and E211" +autopep8 . -a -a -j 0 --recursive --select=E20,E211 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E20 and E211" + +echo "" +echo "PEP8 double aggressive E22, E224, E241, E242 and E27" +autopep8 . -a -a -j 0 --recursive --select=E22,E224,E241,E242,E27 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E22, E224, E241, E242 and E27" + +echo "" +echo "PEP8 double aggressive E225 ~ E228 and E231 and E261" +autopep8 . -a -a -j 0 --recursive --select=E225,E226,E227,E228,E231,E261 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E225 ~ E228 and E231" + +echo "" +echo "PEP8 double aggressive E301 ~ E306" +autopep8 . -a -a -j 0 --recursive --select=E301,E302,E303,E304,E305,E306 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive E301 ~ E306" + +echo "" +echo "PEP8 double aggressive W291 ~ W293 and W391" +autopep8 . -a -a -j 0 --recursive --select=W291,W292,W293,W391 --in-place +git add -u +git add * +git commit -m "PEP8 double aggressive W291 ~ W293 and W391" + +echo "" +finish=$(date +"%s") +timediff=$(($finish-$begin)) +echo -e "Change time was $(($timediff / 60)) minutes and $(($timediff % 60)) seconds." +echo -e "Fast changing would be less than 1 minute." +echo "" +echo "PEP8 Done!" +echo "" diff --git a/CI/build.sh b/CI/build.sh new file mode 100755 index 0000000..3a724d7 --- /dev/null +++ b/CI/build.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# Script by Persian Prince for https://github.com/OpenVisionE2 +# You're not allowed to remove my copyright or reuse this script without putting this header. + +setup_git() { + git config --global user.email "bot@oe-alliance.com" + git config --global user.name "XMLTV-Import python bot" +} + +commit_files() { + git clean -fd + rm -rf *.pyc + rm -rf *.pyo + rm -rf *.mo + git checkout python3 + ./CI/chmod.sh + ./CI/dos2unix.sh + ./CI/PEP8.sh +} + +upload_files() { + git remote add upstream https://${GITHUB_TOKEN}@github.com/oe-alliance/XMLTV-Import.git > /dev/null 2>&1 + git push --quiet upstream python3 || echo "failed to push with error $?" +} + +setup_git +commit_files +upload_files diff --git a/CI/chmod.sh b/CI/chmod.sh new file mode 100755 index 0000000..c16008b --- /dev/null +++ b/CI/chmod.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +echo "" +echo "chmod safe cleanup by Persian Prince" +# Script by Persian Prince for https://github.com/OpenVisionE2 +# You're not allowed to remove my copyright or reuse this script without putting this header. +echo "" +echo "Changing py files, please wait ..." +begin=$(date +"%s") + +echo "" +echo "chmod files" +find . -type d -print0 | xargs -0 chmod 0755 +find . -type f -print0 | xargs -0 chmod 0644 +find . -type f -name "*.sh" -exec chmod +x {} \; +git add -u +git add * +git commit -m "chmod files" + +echo "" +finish=$(date +"%s") +timediff=$(($finish-$begin)) +echo -e "Change time was $(($timediff / 60)) minutes and $(($timediff % 60)) seconds." +echo -e "Fast changing would be less than 1 minute." +echo "" +echo "chmod Done!" +echo "" diff --git a/CI/dos2unix.sh b/CI/dos2unix.sh new file mode 100755 index 0000000..2c86ed7 --- /dev/null +++ b/CI/dos2unix.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +echo "" +echo "dos2unix safe cleanup by Persian Prince" +# Script by Persian Prince for https://github.com/OpenVisionE2 +# You're not allowed to remove my copyright or reuse this script without putting this header. +echo "" +echo "Changing py files, please wait ..." +begin=$(date +"%s") + +echo "" +echo "dos2unix files" +find . -type f \( -iname \*.bb -o -iname \*.conf -o -iname \*.c -o -iname \*.h -o -iname \*.po -o -iname \*.am -o -iname \*.inc -o -iname \*.py -o -iname \*.xml -o -iname \*.sh -o -iname \*.bbappend -o -iname \*.md \) -exec dos2unix {} + +git add -u +git add * +git commit -m "dos2unix files" + +echo "" +finish=$(date +"%s") +timediff=$(($finish-$begin)) +echo -e "Change time was $(($timediff / 60)) minutes and $(($timediff % 60)) seconds." +echo -e "Fast changing would be less than 1 minute." +echo "" +echo "dos2unix Done!" +echo "" diff --git a/README.md b/README.md index 705c90d..da457f0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,17 @@ XMTV-Import =========== -Import's EPG data from rytec xml data sources. \ No newline at end of file +Import's EPG data from rytec xml data sources. + + +## Build status + +[![build](https://github.com/oe-alliance/XMLTV-Import/actions/workflows/build.yml/badge.svg)](https://github.com/oe-alliance/XMLTV-Import/actions/workflows/build.yml.yml) + +## SonarCloud status +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=bugs)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=oe-alliance_XMLTV-Import&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=oe-alliance_XMLTV-Import) diff --git a/src/EPGImport/EPGConfig.py b/src/EPGImport/EPGConfig.py index 1b2bf63..8d92999 100755 --- a/src/EPGImport/EPGConfig.py +++ b/src/EPGImport/EPGConfig.py @@ -1,40 +1,36 @@ -# -*- coding: UTF-8 -*- -from __future__ import absolute_import -from __future__ import print_function -import os -from . import log -from xml.etree.cElementTree import ElementTree, Element, SubElement, tostring, iterparse -import gzip -import time -import random -import six - -from six.moves import cPickle as pickle +from gzip import GzipFile +import lzma +from os import fstat, listdir, remove +from os.path import exists, getmtime, join, split +from pickle import dump, load, HIGHEST_PROTOCOL +from secrets import choice +from time import time +from xml.etree.cElementTree import iterparse +from zipfile import ZipFile +from collections import defaultdict +from . import log # User selection stored here, so it goes into a user settings backup -SETTINGS_FILE = '/etc/enigma2/epgimport.conf' +SETTINGS_FILE = "/etc/enigma2/epgimport.conf" channelCache = {} def isLocalFile(filename): - # we check on a '://' as a silly way to check local file - return '://' not in filename + # we check on a "://" as a silly way to check local file + return "://" not in filename def getChannels(path, name, offset): global channelCache if name in channelCache: return channelCache[name] - dirname, filename = os.path.split(path) + dirname, filename = split(path) if name: - if isLocalFile(name): - channelfile = os.path.join(dirname, name) - else: - channelfile = name + channelfile = join(dirname, name) if isLocalFile(name) else name else: - channelfile = os.path.join(dirname, filename.split('.', 1)[0] + '.channels.xml') + channelfile = join(dirname, filename.split(".", 1)[0] + ".channels.xml") try: return channelCache[channelfile] except KeyError: @@ -44,6 +40,16 @@ def getChannels(path, name, offset): return c +""" +elem.clear() +When you parse an XML file with iterparse(), +the elements are loaded into memory one at a time. However, +if you don't explicitly clear them, +the parser will keep everything in memory until the end of parsing, +which can consume a lot of RAM, especially with large files. +""" + + class EPGChannel: def __init__(self, filename, urls=None, offset=0): self.mtime = None @@ -52,58 +58,83 @@ def __init__(self, filename, urls=None, offset=0): self.urls = [filename] else: self.urls = urls - self.items = None + # self.items = None + self.items = defaultdict(set) self.offset = offset def openStream(self, filename): - fd = open(filename, 'rb') - if not os.fstat(fd.fileno()).st_size: + fd = open(filename, "rb") + if not fstat(fd.fileno()).st_size: raise Exception("File is empty") - if filename.endswith('.gz'): - fd = gzip.GzipFile(fileobj=fd, mode='rb') - elif filename.endswith('.xz') or filename.endswith('.lzma'): - try: - import lzma - except ImportError: - from backports import lzma - fd = lzma.open(filename, 'rb') + if filename.endswith(".gz"): + fd = GzipFile(fileobj=fd, mode="rb") + elif filename.endswith(".xz") or filename.endswith(".lzma"): + fd = lzma.open(filename, "rb") + elif filename.endswith(".zip"): + from io import BytesIO + zip_obj = ZipFile(filename, "r") + fd = BytesIO(zip_obj.open(zip_obj.namelist()[0]).read()) return fd def parse(self, filterCallback, downloadedFile): - print("[EPGImport] Parsing channels from '%s'" % self.name, file=log) - if self.items is None: - self.items = {} + print(f"[EPGImport] Parsing channels from '{self.name}'", file=log) + self.items = defaultdict(set) + try: - context = iterparse(self.openStream(downloadedFile)) - for event, elem in context: - if elem.tag == 'channel': - id = elem.get('id') - id = id.lower() - ref = elem.text - if id and ref: - ref = six.ensure_str(ref) - if filterCallback(ref): - if id in self.items: - self.items[id].append(ref) + stream = self.openStream(downloadedFile) + if stream is None: + print(f"[EPGImport] Error: Unable to open stream for {downloadedFile}", file=log) + return + + # here is a problem in the List of supported formats by iterparse: crash on file corrupt + # _lzma.LZMAError: Input format not supported by decoder + supported_formats = ['.xml', '.xml.gz', '.xml.xz'] # fixed + # Make sure the file is in a compatible format + if any(downloadedFile.endswith(ext) for ext in supported_formats): + + context = iterparse(stream) + for event, elem in context: + if elem.tag == "channel": + channel_id = elem.get("id").lower() + ref = str(elem.text or '').strip() + + if not channel_id or not ref: + continue # Skip empty values + if ref and filterCallback(ref): + """ + if channel_id in self.items: + self.items[channel_id].append(ref) + else: + self.items[channel_id] = [ref] + """ + if channel_id in self.items: + self.items[channel_id].append(ref) + self.items[channel_id] = list(dict.fromkeys(self.items[channel_id])) # Ensure uniqueness else: - self.items[id] = [ref] - elem.clear() + self.items[channel_id] = [ref] + + elem.clear() + except Exception as e: - print("[EPGImport] failed to parse", downloadedFile, "Error:", e, file=log) - pass + print(f"[EPGImport] Failed to parse {downloadedFile} Error: {e}", file=log) + import traceback + traceback.print_exc() def update(self, filterCallback, downloadedFile=None): - customFile = '/etc/epgimport/custom.channels.xml' + customFile = "/etc/epgimport/custom.channels.xml" # Always read custom file since we don't know when it was last updated # and we don't have multiple download from server problem since it is always a local file. - if os.path.exists(customFile): - print("[EPGImport] Parsing channels from '%s'" % customFile, file=log) + if not exists(customFile): + customFile = "/etc/epgimport/rytec.channels.xml" + + if exists(customFile): + print(f"[EPGImport] Parsing channels from '{customFile}'", file=log) self.parse(filterCallback, customFile) if downloadedFile is not None: - self.mtime = time.time() + self.mtime = time() return self.parse(filterCallback, downloadedFile) elif (len(self.urls) == 1) and isLocalFile(self.urls[0]): - mtime = os.path.getmtime(self.urls[0]) + mtime = getmtime(self.urls[0]) if (not self.mtime) or (self.mtime < mtime): self.parse(filterCallback, self.urls[0]) self.mtime = mtime @@ -113,113 +144,118 @@ def downloadables(self): return None else: # Check at most once a day - now = time.time() + now = time() if (not self.mtime) or (self.mtime + 86400 < now): return self.urls return None def __repr__(self): - return "EPGChannel(urls=%s, channels=%s, mtime=%s)" % (self.urls, self.items and len(self.items), self.mtime) + return f"EPGChannel(urls={self.urls}, channels={self.items and len(self.items)}, mtime={self.mtime})" class EPGSource: def __init__(self, path, elem, category=None, offset=0): - self.parser = elem.get('type') - nocheck = elem.get('nocheck') - if nocheck == None: - self.nocheck = 0 - elif nocheck == "1": - self.nocheck = 1 - else: - self.nocheck = 0 - self.urls = [e.text.strip() for e in elem.findall('url')] - self.url = random.choice(self.urls) - self.description = elem.findtext('description') + self.parser = elem.get("type", "gen_xmltv") + self.nocheck = int(elem.get("nocheck", 0)) + self.urls = [e.text.strip() for e in elem.findall("url")] + self.url = choice(self.urls) + self.description = elem.findtext("description", self.url) self.category = category self.offset = offset - if not self.description: - self.description = self.url - self.format = elem.get('format', 'xml') - self.channels = getChannels(path, elem.get('channels'), offset) + self.format = elem.get("format", "xml") + self.channels = getChannels(path, elem.get("channels"), offset) def enumSourcesFile(sourcefile, filter=None, categories=False): global channelCache category = None - for event, elem in iterparse(open(sourcefile, 'rb'), events=("start", "end")): - if event == 'end': - if elem.tag == 'source': - # calculate custom time offset in minutes - offset = int(elem.get('offset', '+0000')) * 3600 // 100 - s = EPGSource(sourcefile, elem, category, offset) - elem.clear() - if (filter is None) or (s.description in filter): - yield s - elif elem.tag == 'channel': - name = elem.get('name') - urls = [e.text.strip() for e in elem.findall('url')] - if name in channelCache: - channelCache[name].urls = urls - else: - channelCache[name] = EPGChannel(name, urls) - elif elem.tag == 'sourcecat': - category = None - elif event == 'start': - # Need the category name sooner than the contents, hence "start" - if elem.tag == 'sourcecat': - category = elem.get('sourcecatname') - if categories: - yield category + try: + with open(sourcefile, "rb") as f: + for event, elem in iterparse(f, events=("start", "end")): + if event == "end": + if elem.tag == "source": + # Calculate custom time offset in minutes + try: + offset = int(elem.get("offset", "+0000")) * 3600 // 100 + except ValueError: + offset = 0 # Default offset if parsing fails + + s = EPGSource(sourcefile, elem, category, offset) + elem.clear() + if filter is None or s.description in filter: + yield s + + elif elem.tag == "channel": + name = elem.get("name") + if name: + urls = [e.text.strip() for e in elem.findall("url")] + if name in channelCache: + channelCache[name].urls = urls + else: + channelCache[name] = EPGChannel(name, urls) + elem.clear() + + elif elem.tag == "sourcecat": + category = None + elem.clear() + + elif event == "start" and elem.tag == "sourcecat": + category = elem.get("sourcecatname") + if categories: + yield category + except Exception as e: + print(f"[EPGImport] Error reading source file: {sourcefile} Error: {e}") def enumSources(path, filter=None, categories=False): try: - for sourcefile in os.listdir(path): - if sourcefile.endswith('.sources.xml'): - sourcefile = os.path.join(path, sourcefile) + for sourcefile in listdir(path): + if sourcefile.endswith(".sources.xml"): + sourcefile = join(path, sourcefile) try: for s in enumSourcesFile(sourcefile, filter, categories): yield s except Exception as e: - print("[EPGImport] failed to open", sourcefile, "Error:", e, file=log) + print(f"[EPGImport] failed to open {sourcefile} Error: {e}", file=log) except Exception as e: - print("[EPGImport] failed to list", path, "Error:", e, file=log) + print(f"[EPGImport] failed to list {path} Error: {e}", file=log) def loadUserSettings(filename=SETTINGS_FILE): try: - return pickle.load(open(filename, 'rb')) + return load(open(filename, "rb")) except Exception as e: - print("[EPGImport] No settings", e, file=log) + print(f"[EPGImport] No settings {e}", file=log) return {"sources": []} def storeUserSettings(filename=SETTINGS_FILE, sources=None): container = {"sources": sources} - pickle.dump(container, open(filename, 'wb'), pickle.HIGHEST_PROTOCOL) + dump(container, open(filename, "wb"), HIGHEST_PROTOCOL) -if __name__ == '__main__': +if __name__ == "__main__": import sys + SETTINGS_FILE = "settings.pkl" x = [] - l = [] - path = '.' + ln = [] + path = "." if len(sys.argv) > 1: path = sys.argv[1] for p in enumSources(path): t = (p.description, p.urls, p.parser, p.format, p.channels, p.nocheck) - l.append(t) + ln.append(t) print(t) x.append(p.description) - storeUserSettings('settings.pkl', [1, "twee"]) - assert loadUserSettings('settings.pkl') == {"sources": [1, "twee"]} - os.remove('settings.pkl') + storeUserSettings(SETTINGS_FILE, [1, "twee"]) + assert loadUserSettings(SETTINGS_FILE) == {"sources": [1, "twee"]} + remove(SETTINGS_FILE) for p in enumSources(path, x): t = (p.description, p.urls, p.parser, p.format, p.channels, p.nocheck) - assert t in l - l.remove(t) - assert not l + assert t in ln + ln.remove(t) + assert not ln for name, c in channelCache.items(): - print("Update:", name) + print(f"Update:{name}") c.update() - print("# of channels:", len(c.items)) + print(f"# of channels: {len(c.items)}") diff --git a/src/EPGImport/EPGImport.py b/src/EPGImport/EPGImport.py index b255b36..ff4aa84 100755 --- a/src/EPGImport/EPGImport.py +++ b/src/EPGImport/EPGImport.py @@ -3,76 +3,74 @@ # This file no longer has a direct link to Enigma2, allowing its use anywhere # you can supply a similar interface. See plugin.py and OfflineImport.py for # the contract. -from datetime import datetime -from os.path import exists, getsize, join, splitext -from os import statvfs, symlink, unlink -import gzip -import random -import time -from . import log -import random -import string -from socket import getaddrinfo, AF_INET6, has_ipv6 - -from twisted.internet import reactor, threads -import twisted.python.runtime +import gzip +from os import statvfs, symlink, unlink +from os.path import exists, getsize, join, splitext from requests import packages, Session -packages.urllib3.disable_warnings(packages.urllib3.exceptions.InsecureRequestWarning) from requests.exceptions import HTTPError, RequestException - +from secrets import choice +from string import ascii_lowercase +from time import localtime, mktime, time +from twisted.internet import reactor, threads from twisted.internet.reactor import callInThread -from twisted.internet import ssl -from twisted.internet._sslverify import ClientTLSOptions -sslverify = False - - -def threadGetPage(url=None, file=None, urlheaders=None, success=None, fail=None, *args, **kwargs): -# print('[EPGImport][threadGetPage] url, file, args, kwargs', url, " ", file, " ", args, " ", kwargs) - try: - s = Session() - s.headers = {} - response = s.get(url, verify=False, headers=urlheaders, timeout=15, allow_redirects=True) - response.raise_for_status() - # check here for content-disposition header so to extract the actual filename (if the url doesnt contain it) - content_disp = response.headers.get('Content-Disposition', '') - filename = content_disp.split('filename="')[-1].split('"')[0] - ext = splitext(file)[1] - if filename: - ext = splitext(filename)[1] - if ext and len(ext) < 6: - file += ext - if not ext: - ext = splitext(response.url)[1] - if ext and len(ext) < 6: - file += ext - - with open(file, "wb") as f: - f.write(response.content) -# print('[EPGImport][threadGetPage] file completed: ', file) - success(file, deleteFile=True) +import twisted.python.runtime - except HTTPError as httperror: - print('EPGImport][threadGetPage] Http error: ', httperror) - fail(httperror) # E0602 undefined name 'error' +from Components.config import config - except RequestException as error: - print('[EPGImport][threadGetPage] error: ', error) -# if fail is not None: - fail(error) +packages.urllib3.disable_warnings(packages.urllib3.exceptions.InsecureRequestWarning) # Used to check server validity -HDD_EPG_DAT = '/hdd/epg.dat' +HDD_EPG_DAT = "/hdd/epg.dat" +if config.misc.epgcache_filename.value: + HDD_EPG_DAT = config.misc.epgcache_filename.value +else: + config.misc.epgcache_filename.setValue(HDD_EPG_DAT) +PARSERS = {"xmltv": "gen_xmltv", "genxmltv": "gen_xmltv"} +# sslverify = False -PARSERS = {'xmltv': 'gen_xmltv', 'genxmltv': 'gen_xmltv'} + +def threadGetPage(url=None, file=None, urlheaders=None, success=None, fail=None, *args, **kwargs): + # print("[EPGImport][threadGetPage] url, file, args, kwargs", url, " ", file, " ", args, " ", kwargs) + try: + s = Session() + s.headers = {} + response = s.get(url, verify=False, headers=urlheaders, timeout=15, allow_redirects=True) + response.raise_for_status() + # check here for content-disposition header so to extract the actual filename (if the url doesnt contain it) + content_disp = response.headers.get("Content-Disposition", "") + filename = content_disp.split('filename="')[-1].split('"')[0] + ext = splitext(file)[1] + if filename: + ext = splitext(filename)[1] + if ext and len(ext) < 6: + file += ext + if not ext: + ext = splitext(response.url)[1] + if ext and len(ext) < 6: + file += ext + + with open(file, "wb") as f: + f.write(response.content) + # print("[EPGImport][threadGetPage] file completed: ", file) + success(file, deleteFile=True) + + except HTTPError as httperror: + print("EPGImport][threadGetPage] Http error: ", httperror) + fail(httperror) # E0602 undefined name "error" + + except RequestException as error: + print("[EPGImport][threadGetPage] error: ", error) + # if fail is not None: + fail(error) def relImport(name): - fullname = __name__.split('.') + fullname = __name__.split(".") fullname[-1] = name - mod = __import__('.'.join(fullname)) + mod = __import__(".".join(fullname)) for n in fullname[1:]: mod = getattr(mod, n) @@ -86,8 +84,28 @@ def getParser(name): def getTimeFromHourAndMinutes(hour, minute): - now = time.localtime() - begin = int(time.mktime((now.tm_year, now.tm_mon, now.tm_mday, hour, minute, 0, now.tm_wday, now.tm_yday, now.tm_isdst))) + # Check if the hour and minute are within valid ranges + if not (0 <= hour < 24): + raise ValueError("Hour must be between 0 and 23") + if not (0 <= minute < 60): + raise ValueError("Minute must be between 0 and 59") + + # Get the current local time + now = localtime() + + # Calculate the timestamp for the specified time (today with the given hour and minute) + begin = int(mktime(( + now.tm_year, # Current year + now.tm_mon, # Current month + now.tm_mday, # Current day + hour, # Specified hour + minute, # Specified minute + 0, # Seconds (set to 0) + now.tm_wday, # Day of the week + now.tm_yday, # Day of the year + now.tm_isdst # Daylight saving time (DST) + ))) + return begin @@ -98,10 +116,12 @@ def bigStorage(minFree, default, *candidates): if free > minFree and free > 50000000: return default except Exception as e: - print("[EPGImport][bigStorage] Failed to stat %s:" % default, e) - with open('/proc/mounts', 'rb') as f: + print(f"[EPGImport][bigStorage] Failed to stat {default}:", e) + + with open("/proc/mounts", "rb") as f: # format: device mountpoint fstype options # - mountpoints = [x.decode().split(' ', 2)[1] for x in f.readlines()] + mountpoints = [x.decode().split(" ", 2)[1] for x in f.readlines()] + for candidate in candidates: if candidate in mountpoints: try: @@ -110,7 +130,7 @@ def bigStorage(minFree, default, *candidates): if free > minFree: return candidate except Exception as e: - print("[EPGImport][bigStorage] Failed to stat %s:" % default, e) + print(f"[EPGImport][bigStorage] Failed to stat {default}:", e) continue raise Exception("[EPGImport][bigStorage] Insufficient storage for download") @@ -137,8 +157,8 @@ def importEvents(self, services, events): def unlink_if_exists(filename): try: unlink(filename) - except: - pass + except Exception as e: + print(f"[EPGImport] warning: Could not remove '{filename}' intermediate", repr(e)) class EPGImport: @@ -160,20 +180,21 @@ def __init__(self, epgcache, channelFilter): def beginImport(self, longDescUntil=None): """Starts importing using Enigma reactor. Set self.sources before calling this.""" - if hasattr(self.epgcache, 'importEvents'): - print('[EPGImport][beginImport] using importEvents.') + if hasattr(self.epgcache, "importEvents"): + print("[EPGImport][beginImport] using importEvents.") self.storage = self.epgcache - elif hasattr(self.epgcache, 'importEvent'): - print('[EPGImport][beginImport] using importEvent(Oudis).') + elif hasattr(self.epgcache, "importEvent"): + print("[EPGImport][beginImport] using importEvent(Oudis).") self.storage = OudeisImporter(self.epgcache) else: - print('[EPGImport][beginImport] oudeis patch not detected, using using epgdat_importer.epgdatclass/epg.dat instead.') + print("[EPGImport][beginImport] oudeis patch not detected, using using epgdat_importer.epgdatclass/epg.dat instead.") from . import epgdat_importer self.storage = epgdat_importer.epgdatclass() + self.eventCount = 0 if longDescUntil is None: # default to 7 days ahead - self.longDescUntil = time.time() + 24 * 3600 * 7 + self.longDescUntil = time() + 24 * 3600 * 7 else: self.longDescUntil = longDescUntil self.nextImport() @@ -183,53 +204,64 @@ def nextImport(self): if not self.sources: self.closeImport() return + self.source = self.sources.pop() - print("[EPGImport][nextImport], source =", self.source.description) + + print(f"[EPGImport][nextImport], source= {self.source.description}") self.fetchUrl(self.source.url) def fetchUrl(self, filename): - if filename.startswith('http:') or filename.startswith('https:') or filename.startswith('ftp:'): -# print("[EPGImport][fetchurl] download Basic ...url filename", filename) + if filename.startswith("http:") or filename.startswith("https:") or filename.startswith("ftp:"): + # print("[EPGImport][fetchurl] download Basic ...url filename", filename) self.urlDownload(filename, self.afterDownload, self.downloadFail) else: self.afterDownload(filename, deleteFile=False) def urlDownload(self, sourcefile, afterDownload, downloadFail): - host = ''.join(random.choices(string.ascii_lowercase, k=5)) + media_path = "/media/hdd" + host = "".join([choice(ascii_lowercase) for i in range(5)]) check_mount = False - if exists("/media/hdd"): - with open('/proc/mounts', 'r') as f: + if exists(media_path): + with open("/proc/mounts", "r") as f: for line in f: - l = line.split() - if len(l) > 1 and l[1] == '/media/hdd': + ln = line.split() + if len(ln) > 1 and ln[1] == media_path: check_mount = True + # print("[EPGImport][urlDownload]2 check_mount ", check_mount) - pathDefault = "/media/hdd" if check_mount else "/tmp" - path = bigStorage(9000000, pathDefault, '/media/usb', '/media/cf') # lets use HDD and flash as main backup media + pathDefault = media_path if check_mount else "/tmp" + path = bigStorage(9000000, pathDefault, "/media/usb", "/media/cf") # lets use HDD and flash as main backup media + filename = join(path, host) + ext = splitext(sourcefile)[1] # Keep sensible extension, in particular the compression type if ext and len(ext) < 6: filename += ext Headers = { - 'User-Agent': 'Twisted Client', - 'Accept-Encoding': 'gzip, deflate', - 'Accept': '*/*', - 'Connection': 'keep-alive'} - print("[EPGImport][urlDownload] Downloading: " + sourcefile + " to local path: " + filename) + "User-Agent": "Twisted Client", + "Accept-Encoding": "gzip, deflate", + "Accept": "*/*", + "Connection": "keep-alive"} + + print(f"[EPGImport][urlDownload] Downloading: {sourcefile} to local path: {filename}") callInThread(threadGetPage, url=sourcefile, file=filename, urlheaders=Headers, success=afterDownload, fail=downloadFail) def afterDownload(self, filename, deleteFile=False): -# print("[EPGImport][afterDownload] filename", filename) + # print("[EPGImport][afterDownload] filename", filename) + if not exists(filename): + print(f"[EPGImport][afterDownload] File not found: {filename}") + self.downloadFail("File not exists") + return try: if not getsize(filename): raise Exception("[EPGImport][afterDownload] File is empty") except Exception as e: - print("[EPGImport][afterDownload] Exception filename 0", filename) + print(f"[EPGImport][afterDownload] Exception filename 0 {filename}") self.downloadFail(e) return - if self.source.parser == 'epg.dat': + if self.source.parser == "epg.dat": if twisted.python.runtime.platform.supportsThreads(): print("[EPGImport][afterDownload] Using twisted thread for DAT file") threads.deferToThread(self.readEpgDatFile, filename, deleteFile).addCallback(lambda ignore: self.nextImport()) @@ -237,79 +269,87 @@ def afterDownload(self, filename, deleteFile=False): self.readEpgDatFile(filename, deleteFile) return - if filename.endswith('.gz'): - self.fd = gzip.open(filename, 'rb') - try: # read a bit to make sure it's a gzip file - file_content = self.fd.peek(1) + if filename.endswith(".gz"): + self.fd = gzip.open(filename, "rb") + try: # read a bit to make sure it's a gzip file + # file_content = self.fd.peek(1) # file_content ??? + self.fd.read(10) + self.fd.seek(0, 0) except gzip.BadGzipFile as e: - print("[EPGImport][afterDownload] File downloaded is not a valid gzip file", filename) + print(f"[EPGImport][afterDownload] File downloaded is not a valid gzip file {filename}") + try: + print(f"[EPGImport][afterDownload] unlink {filename}") + unlink_if_exists(filename) + except Exception as e: + print(f"[EPGImport][afterDownload] warning: Could not remove '{filename}' intermediate", str(e)) self.downloadFail(e) return - elif filename.endswith('.xz') or filename.endswith('.lzma'): + elif filename.endswith(".xz") or filename.endswith(".lzma"): try: import lzma except ImportError: from backports import lzma - - self.fd = lzma.open(filename, 'rb') - try: # read a bit to make sure it's an xz file - file_content = self.fd.peek(1) + self.fd = lzma.open(filename, "rb") + try: # read a bit to make sure it's an xz file + # file_content = self.fd.peek(1) # file_content ??? + self.fd.read(10) + self.fd.seek(0, 0) except lzma.LZMAError as e: - print("[EPGImport][afterDownload] File downloaded is not a valid xz file", filename) + print(f"[EPGImport][afterDownload] File downloaded is not a valid xz file {filename}") try: - print("[EPGImport][afterDownload] unlink", filename) - unlink(filename) + print(f"[EPGImport][afterDownload] unlink {filename}") + unlink_if_exists(filename) except Exception as e: - print("[EPGImport][afterDownload] warning: Could not remove '%s' intermediate" % filename, e) + print(f"[EPGImport][afterDownload] warning: Could not remove '{filename}' intermediate", e) self.downloadFail(e) return else: - self.fd = open(filename, 'rb') + self.fd = open(filename, "rb") - if deleteFile and self.source.parser != 'epg.dat': + if deleteFile and self.source.parser != "epg.dat": try: - print("[EPGImport][afterDownload] unlink", filename) - unlink(filename) + print(f"[EPGImport][afterDownload] unlink {filename}") + unlink_if_exists(filename) except Exception as e: - print("[EPGImport][afterDownload] warning: Could not remove '%s' intermediate" % filename, e) + print(f"[EPGImport][afterDownload] warning: Could not remove '{filename}' intermediate", e) self.channelFiles = self.source.channels.downloadables() -# print("[EPGImport][afterDownload] self.source, self.channelFiles", self.source, " ", self.channelFiles) + # print("[EPGImport][afterDownload] self.source, self.channelFiles", self.source, " ", self.channelFiles) if not self.channelFiles: self.afterChannelDownload(None, None) else: - filename = random.choice(self.channelFiles) + filename = choice(self.channelFiles) self.channelFiles.remove(filename) -# print("[EPGImport][afterDownload] download Channels ...filename", filename) + # print("[EPGImport][afterDownload] download Channels ...filename", filename) self.urlDownload(filename, self.afterChannelDownload, self.channelDownloadFail) return def downloadFail(self, failure): - print("[EPGImport][downloadFail] download failed:", failure) + print(f"[EPGImport][downloadFail] download failed: {failure}") + # if self.source.url in self.source.urls: # use this ;) self.source.urls.remove(self.source.url) if self.source.urls: print("[EPGImport][downloadFail] Attempting alternative URL for Basic") - self.source.url = random.choice(self.source.urls) - print("[EPGImport][downloadFail] try alternative download url", self.source.url) + self.source.url = choice(self.source.urls) + print(f"[EPGImport][downloadFail] try alternative download url {self.source.url}") self.fetchUrl(self.source.url) else: self.nextImport() def afterChannelDownload(self, filename, deleteFile=True): -# print("[EPGImport][afterChannelDownload] filename", filename) + # print("[EPGImport][afterChannelDownload] filename", filename) if filename: try: if not getsize(filename): raise Exception("File is empty") except Exception as e: - print("[EPGImport][afterChannelDownload] Exception filename", filename) + print(f"[EPGImport][afterChannelDownload] Exception filename {filename}") self.channelDownloadFail(e) return - if twisted.python.runtime.platform.supportsThreads(): - print("[EPGImport][afterChannelDownload] Using twisted thread - filename ", filename) + print(f"[EPGImport][afterChannelDownload] Using twisted thread - filename {filename}") threads.deferToThread(self.doThreadRead, filename).addCallback(lambda ignore: self.nextImport()) deleteFile = False # Thread will delete it else: @@ -317,49 +357,54 @@ def afterChannelDownload(self, filename, deleteFile=True): reactor.addReader(self) if deleteFile and filename: try: - unlink(filename) + unlink_if_exists(filename) except Exception as e: - print("[EPGImport][afterChannelDownload] warning: Could not remove '%s' intermediate" % filename, e) + print(f"[EPGImport][afterChannelDownload] warning: Could not remove '{filename}' intermediate", e, file=log) def channelDownloadFail(self, failure): - print("[EPGImport][channelDownloadFail] download channel failed:", failure) + print(f"[EPGImport][channelDownloadFail] download channel failed: {failure}") if self.channelFiles: - filename = random.choice(self.channelFiles) + filename = choice(self.channelFiles) self.channelFiles.remove(filename) - print("[EPGImport][channelDownloadFail] retry alternative download channel - new url filename", filename) + print(f"[EPGImport][channelDownloadFail] retry alternative download channel - new url filename {filename}") self.urlDownload(filename, self.afterChannelDownload, self.channelDownloadFail) else: print("[EPGImport][channelDownloadFail] no more alternatives for channels") self.nextImport() def createIterator(self, filename): -# print("[EPGImport][createIterator], filename", filename) + # print("[EPGImport][createIterator], filename", filename) self.source.channels.update(self.channelFilter, filename) return getParser(self.source.parser).iterator(self.fd, self.source.channels.items, self.source.offset) def readEpgDatFile(self, filename, deleteFile=False): - if not hasattr(self.epgcache, 'load'): + if not hasattr(self.epgcache, "load"): print("[EPGImport] Cannot load EPG.DAT files on unpatched enigma. Need CrossEPG patch.") return + unlink_if_exists(HDD_EPG_DAT) + try: - if filename.endswith('.gz'): - print("[EPGImport] Uncompressing", filename) + if filename.endswith(".gz"): + print(f"[EPGImport] Uncompressing {filename}") import shutil - fd = gzip.open(filename, 'rb') - epgdat = open(HDD_EPG_DAT, 'wb') + fd = gzip.open(filename, "rb") + epgdat = open(HDD_EPG_DAT, "wb") shutil.copyfileobj(fd, epgdat) del fd epgdat.close() del epgdat + elif filename != HDD_EPG_DAT: symlink(filename, HDD_EPG_DAT) - print("[EPGImport] Importing", HDD_EPG_DAT) + + print(f"[EPGImport] Importing {HDD_EPG_DAT}") self.epgcache.load() + if deleteFile: unlink_if_exists(filename) except Exception as e: - print("[EPGImport] Failed to import %s:" % filename, e) + print(f"[EPGImport] Failed to import {filename}:", e) def fileno(self): if self.fd is not None: @@ -375,48 +420,42 @@ def doThreadRead(self, filename): r, d = data if d[0] > self.longDescUntil: # Remove long description (save RAM memory) - d = d[:4] + ('',) + d[5:] + d = d[:4] + ("",) + d[5:] try: self.storage.importEvents(r, (d,)) except Exception as e: - import traceback - print("[EPGImport][doThreadRead] ### importEvents exception:", e) + print(f"[EPGImport][doThreadRead] ### importEvents exception: {e}") print("[EPGImport][doThreadRead] ### thread is ready ### Events:", self.eventCount) if filename: try: - unlink(filename) + unlink_if_exists(filename) except Exception as e: - print("[EPGImport] warning: Could not remove '%s' intermediate" % filename, e) - + print(f"[EPGImport] warning: Could not remove '{filename}' intermediate %s" % e) return def doRead(self): """called from reactor to read some data""" try: # returns tuple (ref, data) or None when nothing available yet. - # data = self.iterator.next() data = next(self.iterator) - if data is not None: self.eventCount += 1 try: r, d = data if d[0] > self.longDescUntil: # Remove long description (save RAM memory) - d = d[:4] + ('',) + d[5:] + d = d[:4] + ("",) + d[5:] self.storage.importEvents(r, (d,)) except Exception as e: - print("[EPGImport][doRead] importEvents exception:", e) - + print(f"[EPGImport][doRead] importEvents exception: {e}") except StopIteration: self.nextImport() - return def connectionLost(self, failure): """called from reactor on lost connection""" # This happens because enigma calls us after removeReader - print("[EPGImport] connectionLost", failure) + print(f"[EPGImport] connectionLost {failure}") def closeReader(self): if self.fd is not None: @@ -430,20 +469,22 @@ def closeImport(self): self.closeReader() self.iterator = None self.source = None - if hasattr(self.storage, 'epgfile'): + if hasattr(self.storage, "epgfile"): needLoad = self.storage.epgfile else: needLoad = None + self.storage = None + if self.eventCount is not None: - print("[EPGImport] imported %d events" % self.eventCount) + print(f"[EPGImport] imported {self.eventCount} events") reboot = False if self.eventCount: if needLoad: - print("[EPGImport] no Oudeis patch, load(%s) required" % needLoad) + print(f"[EPGImport] no Oudeis patch, load({needLoad}) required") reboot = True try: - if hasattr(self.epgcache, 'load'): + if hasattr(self.epgcache, "load"): print("[EPGImport] attempt load() patch") if needLoad != HDD_EPG_DAT: symlink(needLoad, HDD_EPG_DAT) @@ -451,17 +492,15 @@ def closeImport(self): reboot = False unlink_if_exists(needLoad) except Exception as e: - print("[EPGImport] load() failed:", e) - - elif hasattr(self.epgcache, 'save'): + print(f"[EPGImport] load() failed: {e}") + elif hasattr(self.epgcache, "save"): self.epgcache.save() - elif hasattr(self.epgcache, 'timeUpdated'): + elif hasattr(self.epgcache, "timeUpdated"): self.epgcache.timeUpdated() if self.onDone: self.onDone(reboot=reboot, epgfile=needLoad) self.eventCount = None print("[EPGImport] #### Finished ####") - return def isImportRunning(self): return self.source is not None diff --git a/src/EPGImport/ExpandableSelectionList.py b/src/EPGImport/ExpandableSelectionList.py index 73eb368..25d68cd 100644 --- a/src/EPGImport/ExpandableSelectionList.py +++ b/src/EPGImport/ExpandableSelectionList.py @@ -1,9 +1,9 @@ +from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT from Components.MenuList import MenuList +from skin import fonts, parameters from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN -from enigma import eListboxPythonMultiContent, eListbox, gFont, RT_HALIGN_LEFT from Tools.LoadPixmap import LoadPixmap -import skin expandableIcon = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "icons/expandable.png")) expandedIcon = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "icons/expanded.png")) @@ -11,21 +11,18 @@ def loadSettings(): global cat_desc_loc, entry_desc_loc, cat_icon_loc, entry_icon_loc - x, y, w, h = skin.parameters.get("EPGImportSelectionListDescr", skin.parameters.get("SelectionListDescr", (25, 3, 650, 30))) - ind = x # Indent the entries by the same amount as the icon. + x, y, w, h = parameters.get("EPGImportSelectionListDescr", parameters.get("SelectionListDescr", (25, 3, 650, 30))) + ind = x # Indent the entries by the same amount as the icon. cat_desc_loc = (x, y, w, h) entry_desc_loc = (x + ind, y, w - ind, h) - x, y, w, h = skin.parameters.get("EPGImportSelectionListLock", skin.parameters.get("SelectionListLock", (0, 2, 25, 24))) - cat_icon_loc = (x, 0, w, y + y + h) # The category icon is larger + x, y, w, h = parameters.get("EPGImportSelectionListLock", parameters.get("SelectionListLock", (0, 2, 25, 24))) + cat_icon_loc = (x, 0, w, y + y + h) # The category icon is larger entry_icon_loc = (x + ind, y, w, h) def category(description, isExpanded=False): global cat_desc_loc, cat_icon_loc - if isExpanded: - icon = expandedIcon - else: - icon = expandableIcon + icon = expandedIcon if isExpanded else expandableIcon return [ (description, isExpanded, []), (eListboxPythonMultiContent.TYPE_TEXT,) + cat_desc_loc + (0, RT_HALIGN_LEFT, description), @@ -65,14 +62,14 @@ def isExpanded(cat): def isCategory(item): # Return whether list enty is a Category - return hasattr(item[0][2], 'append') + return hasattr(item[0][2], "append") class ExpandableSelectionList(MenuList): def __init__(self, tree=None, enableWrapAround=False): - 'tree is expected to be a list of categories' + "tree is expected to be a list of categories" MenuList.__init__(self, [], enableWrapAround, content=eListboxPythonMultiContent) - font = skin.fonts.get("SelectionList", ("Regular", 20, 30)) + font = fonts.get("SelectionList", ("Regular", 20, 30)) self.l.setFont(0, gFont(font[0], font[1])) self.l.setItemHeight(font[2]) self.tree = tree or [] @@ -80,13 +77,13 @@ def __init__(self, tree=None, enableWrapAround=False): def updateFlatList(self): # Update the view of the items by flattening the tree - l = [] + ln = [] for cat in self.tree: - l.append(cat) + ln.append(cat) if isExpanded(cat): for item in cat[0][2]: - l.append(entry(*item)) - self.setList(l) + ln.append(entry(*item)) + self.setList(ln) def toggleSelection(self): idx = self.getSelectedIndex() diff --git a/src/EPGImport/OfflineImport.py b/src/EPGImport/OfflineImport.py index 287e748..5df8394 100755 --- a/src/EPGImport/OfflineImport.py +++ b/src/EPGImport/OfflineImport.py @@ -14,11 +14,10 @@ # 2) At the command line go to the parent directory of EPGImport: # 3) cd /usr/lib/enigma2/python/Plugins/Extensions # 4) Now run as a module from the command line: -# 5) python -m EPGImport.OfflineImport e.g. python -m EPGImport.OfflineImport /etc/rytec.sources.xml (> /tmp.log) +# 5) python -m EPGImport.OfflineImport e.g. python -m EPGImport.OfflineImport /etc/rytec.sources.xml (> /tmp.log) # 6) Reinstate your renamed __init__.py # # called modules EPGImport, epgdat, epgdat_importer, log -import os import sys import time from . import EPGConfig @@ -32,10 +31,13 @@ class FakeEnigma: def getInstance(self): return self -# def load(self): -# print("...load...") -# def importEvents(self, *args): -# print(args) + + """ + def load(self): + print("...load...") + def importEvents(self, *args): + print(args) + """ def importFrom(epgimport, sourceXml): @@ -69,14 +71,16 @@ def stop(self): epgimport.beginImport(longDescUntil=time.time() + (5 * 24 * 3600)) EPGImport.reactor.run() -#---------------------------------------------- +# ---------------------------------------------- def done(reboot=False, epgfile=None): EPGImport.reactor.stop() print("Done, data is in", epgfile) - ### When code arrives here, EPG data is stored in filename EPGImport.HDD_EPG_DAT - ### So to copy it to FTP or whatever, this is the place to add that code. + """ + When code arrives here, EPG data is stored in filename EPGImport.HDD_EPG_DAT + So to copy it to FTP or whatever, this is the place to add that code. + """ if len(sys.argv) <= 1: diff --git a/src/EPGImport/__init__.py b/src/EPGImport/__init__.py index 2e4b2d9..ea769c7 100755 --- a/src/EPGImport/__init__.py +++ b/src/EPGImport/__init__.py @@ -1,24 +1,22 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function from Components.Language import language -from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_LANGUAGE -import os -import gettext +from Tools.Directories import resolveFilename, SCOPE_PLUGINS +from gettext import bindtextdomain, dgettext, gettext PluginLanguageDomain = "EPGImport" PluginLanguagePath = "Extensions/EPGImport/locale" def localeInit(): - gettext.bindtextdomain(PluginLanguageDomain, resolveFilename(SCOPE_PLUGINS, PluginLanguagePath)) + bindtextdomain(PluginLanguageDomain, resolveFilename(SCOPE_PLUGINS, PluginLanguagePath)) def _(txt): - if gettext.dgettext(PluginLanguageDomain, txt): - return gettext.dgettext(PluginLanguageDomain, txt) + if dgettext(PluginLanguageDomain, txt): + return dgettext(PluginLanguageDomain, txt) else: - print("[" + PluginLanguageDomain + "] fallback to default translation for " + txt) - return gettext.gettext(txt) + print(f"[{PluginLanguageDomain}] fallback to default translation for {txt}") + return gettext(txt) + localeInit() language.addCallback(localeInit) diff --git a/src/EPGImport/boot.py b/src/EPGImport/boot.py deleted file mode 100644 index e8acf2b..0000000 --- a/src/EPGImport/boot.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python -from __future__ import print_function -import os -import time -import shutil - -MEDIA = ("/media/hdd/", "/media/usb/", "/media/mmc/", "/media/cf/", "/tmp") - - -def findEpg(): - candidates = [] - for path in MEDIA: - try: - if os.path.exists(path): - for fn in os.listdir(path): - if "epg.dat" in fn: - ffn = os.path.join(path, fn) - candidates.append((os.path.getctime(ffn), ffn)) - except: - pass # ignore errors. - if not candidates: - return None - candidates.sort() # order by ctime... - # best candidate is most recent filename. - return candidates[-1][1] - - -def checkCrashLog(): - for path in MEDIA[:-1]: - try: - dirList = os.listdir(path) - for fname in dirList: - if fname[0:13] == 'enigma2_crash': - try: - crashtime = 0 - crashtime = int(fname[14:24]) - howold = time.time() - crashtime - except: - print("no time found in filename") - if howold < 120: - print("recent crashfile found analysing") - crashfile = open(path + fname, "r") - crashtext = crashfile.read() - crashfile.close() - if (crashtext.find("FATAL: LINE ") != -1): - print("string found, deleting epg.dat") - return True - except: - pass - return False - - -def findNewEpg(): - for path in MEDIA: - fn = os.path.join(path, 'epg_new.dat') - if os.path.exists(fn): - return fn - - -epg = findEpg() -newepg = findNewEpg() - -print("Epg.dat found at : ", epg) -print("newepg found at : ", newepg) - -##Delete epg.dat if last crash was because of error in epg.dat -if checkCrashLog(): - try: - os.unlink(epg) - except: - print("delete error") - -##if excists cp epg_new.dat epg.dat -if newepg: - if epg: - print("replacing epg.dat with newmade version") - os.unlink(epg) - shutil.copy2(newepg, epg) diff --git a/src/EPGImport/epgdat.py b/src/EPGImport/epgdat.py index ec2e2a3..8ad6e35 100755 --- a/src/EPGImport/epgdat.py +++ b/src/EPGImport/epgdat.py @@ -14,7 +14,7 @@ from . import dreamcrc crc32_dreambox = lambda d, t: dreamcrc.crc32(d, t) & 0xffffffff print("[EPGImport] using C module, yay") -except: +except ImportError: print("[EPGImport] failed to load C implementation, sorry") # this table is used by CRC32 routine below (used by Dreambox for @@ -94,14 +94,15 @@ def crc32_dreambox(crcdata, crctype, crctable=CRCTABLE): # ML Optimized: local CRCTABLE (locals are faster), remove self, remove code that has no effect, faster loop - #crc=0x00000000 - #crc=((crc << 8 ) & 0xffffff00) ^ crctable[((crc >> 24) ^ crctype) & 0x000000ff] + # crc=0x00000000 + # crc=((crc << 8 ) & 0xffffff00) ^ crctable[((crc >> 24) ^ crctype) & 0x000000ff] crc = crctable[crctype & 0x000000ff] crc = ((crc << 8) & 0xffffff00) ^ crctable[((crc >> 24) ^ len(crcdata)) & 0x000000ff] for d in crcdata: crc = ((crc << 8) & 0xffffff00) ^ crctable[((crc >> 24) ^ d) & 0x000000ff] return crc + # convert time or duration from datetime format to 3 bytes hex value # this doesn't convert to hex but obviously the originators thought it did, and is part of EPG structure definitions. def TL_hexconv(dt): @@ -109,11 +110,10 @@ def TL_hexconv(dt): (dt.hour % 10) + (16 * (dt.hour // 10)), (dt.minute % 10) + (16 * (dt.minute // 10)), (dt.second % 10) + (16 * (dt.second // 10)) - ) + ) class epgdat_class: - # temp files used for EPG.DAT creation LAMEDB = '/etc/enigma2/lamedb' @@ -174,16 +174,16 @@ def set_excludedsid(self, exsidlist): # assembling short description (type 0x4d , it's the Title) and compute its crc def shortDescription(self, sd): - sdbytes = sd.encode() + sdbytes = sd.encode() beng = "eng".encode() - b0 = "\0".encode() + b0 = "\0".encode() # 0x15 is UTF-8 encoding. sdbin = self.s_3sBB.pack(beng, int(len(sdbytes) + 1), 0x15) + sdbytes + b0 return (crc32_dreambox(sdbin, 0x4d), sdbin) # assembling long description (type 0x4e) and compute its crc def longDescription(self, ld): - beng = "eng".encode() + beng = "eng".encode() ldres = [] # compute total number of descriptions, block 245 bytes each # number of descriptions start to index 0 @@ -191,7 +191,7 @@ def longDescription(self, ld): num_tot_desc = (len(ldbytes) + 244) // 245 for i in range(num_tot_desc): ssub = ldbytes[i * 245:i * 245 + 245] - ldbin = self.s_B3sBBB.pack((i << 4) + (num_tot_desc-1), beng, 0x00, int(len(ssub) + 1), 0x15) + ssub + ldbin = self.s_B3sBBB.pack((i << 4) + (num_tot_desc - 1), beng, 0x00, int(len(ssub) + 1), 0x15) + ssub ldres.append((crc32_dreambox(ldbin, 0x4e), ldbin)) return ldres @@ -219,59 +219,60 @@ def preprocess_events_channel(self, services): s_I = self.s_I for event in events: # **** (1) : create DESCRIPTION HEADER / DATA **** - + EPG_EVENT_HEADER_datasize = 0 - - # short description (title) type 0x4d self.shortDescription(title[:240]) = event[2] - shortDescription = event[2] # (crc32, short description packed) - EPG_EVENT_HEADER_datasize += 4 # add 4 bytes for a single REF DESC (CRC32) + + # short description (title) type 0x4d self.shortDescription(title[:240]) = event[2] + shortDescription = event[2] # (crc32, short description packed) + EPG_EVENT_HEADER_datasize += 4 # add 4 bytes for a single REF DESC (CRC32) if shortDescription[0] not in list(self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER.keys()): - pack_1 = s_BB.pack(0x4d, int(len(shortDescription[1]))) + shortDescription[1] # DESCRIPTION DATA + pack_1 = s_BB.pack(0x4d, int(len(shortDescription[1]))) + shortDescription[1] # DESCRIPTION DATA # DESCRIPTION HEADER (2 int) will be computed at the end just before EPG.DAT write # because it needs the total number of the same descriptions called by any channel section - self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[shortDescription[0]] = [pack_1, 1] # save CRC32 and short description data packed + self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[shortDescription[0]] = [pack_1, 1] # save CRC32 and short description data packed self.EPG_HEADER2_description_count += 1 else: - #increment_event(shortDescription[0]) + # increment_event(shortDescription[0]) self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[shortDescription[0]][1] += 1 - - # long description type 0x4e self.longDescription(description) = event[3] - longDescription = event[3] # (crc32, long description(s) packed) - EPG_EVENT_HEADER_datasize += 4 * len(longDescription) # add 4 bytes for each CRC32 - for desc in longDescription: # desc = crc + packed long desc + + # long description type 0x4e self.longDescription(description) = event[3] + longDescription = event[3] # (crc32, long description(s) packed) + EPG_EVENT_HEADER_datasize += 4 * len(longDescription) # add 4 bytes for each CRC32 + for desc in longDescription: # desc = crc + packed long desc if desc[0] not in list(self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER.keys()): # DESCRIPTION DATA - pack_2 = s_BB.pack(0x4e, int(len(desc[1]))) + desc[1] # short description + pack_2 = s_BB.pack(0x4e, int(len(desc[1]))) + desc[1] # short description self.EPG_HEADER2_description_count += 1 # DESCRIPTION HEADER (2 int) will be computed at the end just before EPG.DAT write # because it need the total number of the same description called by different channel section - #save_event(longDescription[i][0],[pack_1,1]) - self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[desc[0]] = [pack_2, 1] # save crc32 and description packed + # save_event(longDescription[i][0],[pack_1,1]) + self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[desc[0]] = [pack_2, 1] # save crc32 and description packed else: - #increment_event(longDescription[i][0]) + # increment_event(longDescription[i][0]) self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[desc[0]][1] += 1 - + # **** (2) : have all crc32's and now can create EVENT HEADER / DATA **** # EVENT HEADER (3 bytes: 0x01 , 0x00, 10 bytes + number of CRC32 * 4) - pack_3 = s_BBB.pack(0x01, 0x00, 0x0a + EPG_EVENT_HEADER_datasize) + pack_3 = s_BBB.pack(0x01, 0x00, 0x0a + EPG_EVENT_HEADER_datasize) self.EPG_TMP_FD.write(pack_3) - + # extract date and time from event numbers are seconds # unix format (second since 1970) and already GMT corrected - event_time_HMS = datetime.utcfromtimestamp(event[0]) # actually YYYY-MM-DD HH:MM:SS - dvb_date = event_time_HMS.toordinal() - self.EPG_PROLEPTIC_ZERO_DAY # epg.dat date is = (proleptic date - epg_zero_day) - event_duration_HMS = datetime.utcfromtimestamp(event[1]) # actually 1970-01-01 HH:MM:SS + event_time_HMS = datetime.utcfromtimestamp(event[0]) # actually YYYY-MM-DD HH:MM:SS + dvb_date = event_time_HMS.toordinal() - self.EPG_PROLEPTIC_ZERO_DAY # epg.dat date is = (proleptic date - epg_zero_day) + # event_duration_HMS = datetime.utcfromtimestamp(event[1]) # actually 1970-01-01 HH:MM:SS + event_duration_HMS = datetime.datetime(*time.gmtime(event[1])[:6]) # actually 1970-01-01 HH:MM:SS # EVENT DATA # simply create an incremental ID, starting from '1' # event_id appears to be per channel, so this should be okay. EPG_EVENT_DATA_id += 1 - pack_4 = self.s_b_HH.pack(EPG_EVENT_DATA_id, dvb_date) # ID and DATE , always in BIG_ENDIAN - pack_5 = s_BBB.pack(*TL_hexconv(event_time_HMS)) # Start time - pack_6 = s_BBB.pack(*TL_hexconv(event_duration_HMS)) # Duration - pack_7 = s_I.pack(shortDescription[0]) # REF DESC crc short (title) + pack_4 = self.s_b_HH.pack(EPG_EVENT_DATA_id, dvb_date) # ID and DATE , always in BIG_ENDIAN + pack_5 = s_BBB.pack(*TL_hexconv(event_time_HMS)) # Start time + pack_6 = s_BBB.pack(*TL_hexconv(event_duration_HMS)) # Duration + pack_7 = s_I.pack(shortDescription[0]) # REF DESC crc short (title) for description in longDescription: - pack_7 += s_I.pack(description[0]) # REF DESC long + pack_7 += s_I.pack(description[0]) # REF DESC long self.EPG_TMP_FD.write(pack_4 + pack_5 + pack_6 + pack_7) # reset again event container self.EPG_TOTAL_EVENTS += len(self.events) @@ -302,9 +303,9 @@ def final_process(self): # event MUST BE WRITTEN IN ASCENDING ORDERED using HASH CODE as index for temp in sorted(list(self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER.keys())): temp_crc_data = self.EPGDAT_HASH_EVENT_MEMORY_CONTAINER[temp] - #pack_4=struct.pack(LB_ENDIAN+"II",int(temp,16),temp_crc_data[1]) - pack_4 = s_ii.pack(temp, temp_crc_data[1]) # crc and packed data - epgdat_fd.write(pack_4 + temp_crc_data[0]) # packed (crc, packed data) & crc + # pack_4=struct.pack(LB_ENDIAN+"II",int(temp,16),temp_crc_data[1]) + pack_4 = s_ii.pack(temp, temp_crc_data[1]) # crc and packed data + epgdat_fd.write(pack_4 + temp_crc_data[0]) # packed (crc, packed data) & crc epgdat_fd.close() # *** cleanup ** if os.path.exists(self.EPGDAT_TMP_FILENAME): diff --git a/src/EPGImport/epgdat_importer.py b/src/EPGImport/epgdat_importer.py index 5361204..fbbbd0d 100755 --- a/src/EPGImport/epgdat_importer.py +++ b/src/EPGImport/epgdat_importer.py @@ -1,15 +1,16 @@ +from os import popen +from os.path import join +from sys import platform from . import epgdat -import os -import sys -import sys + # Hack to make this test run on Windows (where the reactor cannot handle files) -if sys.platform.startswith('win'): - tmppath = '.' - settingspath = '.' +if platform.startswith("win"): + tmppath = "." + settingspath = "." else: - tmppath = '/tmp' - settingspath = '/etc/enigma2' + tmppath = "/tmp" + settingspath = "/etc/enigma2" class epgdatclass: @@ -17,25 +18,26 @@ def __init__(self): self.data = None self.services = None path = tmppath - if self.checkPath('/media/cf'): - path = '/media/cf' - if self.checkPath('/media/mmc'): - path = '/media/mmc' - if self.checkPath('/media/usb'): - path = '/media/usb' - if self.checkPath('/media/hdd'): - path = '/media/hdd' - self.epgfile = os.path.join(path, 'epg_new.dat') + if self.checkPath("/media/cf"): + path = "/media/cf" + if self.checkPath("/media/mmc"): + path = "/media/mmc" + if self.checkPath("/media/usb"): + path = "/media/usb" + if self.checkPath("/media/hdd"): + path = "/media/hdd" + self.epgfile = join(path, "epg_new.dat") + self.epg = epgdat.epgdat_class(path, settingspath, self.epgfile) def importEvents(self, services, dataTupleList): - 'This method is called repeatedly for each bit of data' + '''This method is called repeatedly for each bit of data''' if services != self.services: self.commitService() self.services = services for program in dataTupleList: if program[3]: - desc = program[3] + '\n' + program[4] + desc = f"{program[3]}\n{program[4]}" else: desc = program[4] self.epg.add_event(program[0], program[1], program[2], desc) @@ -55,13 +57,13 @@ def epg_done(self): self.epg = None def checkPath(self, path): - f = os.popen('mount', "r") - for l in f: - if l.find(path) != -1: + f = popen("mount", "r") + for ln in f: + if ln.find(path) != - 1: return True return False def __del__(self): - 'Destructor - finalize the file when done' + """Destructor - finalize the file when done""" if self.epg is not None: self.epg_done() diff --git a/src/EPGImport/filtersServices.py b/src/EPGImport/filtersServices.py index 696a185..5e7748e 100644 --- a/src/EPGImport/filtersServices.py +++ b/src/EPGImport/filtersServices.py @@ -1,19 +1,16 @@ -from __future__ import absolute_import from . import _ from Screens.Screen import Screen from Screens.MessageBox import MessageBox from Screens.ChoiceBox import ChoiceBox from Components.ActionMap import ActionMap from ServiceReference import ServiceReference -from Screens.ChannelSelection import service_types_radio, service_types_tv, ChannelSelection, ChannelSelectionBase -from enigma import eServiceReference, eServiceCenter, iServiceInformation +from Screens.ChannelSelection import service_types_radio, service_types_tv, ChannelSelectionBase +from enigma import eServiceReference, eServiceCenter from Components.Sources.List import List from Components.Label import Label from . import EPGConfig import os -from six.moves import reload_module - OFF = 0 EDIT_BOUQUET = 1 @@ -27,14 +24,14 @@ def getProviderName(ref): provider_root = eServiceReference(rootstr) serviceHandler = eServiceCenter.getInstance() providerlist = serviceHandler.list(provider_root) - if not providerlist is None: + if providerlist is not None: while True: provider = providerlist.getNext() if not provider.valid(): break if provider.flags & eServiceReference.isDirectory: servicelist = serviceHandler.list(provider) - if not servicelist is None: + if servicelist is not None: while True: service = servicelist.getNext() if not service.valid(): @@ -62,7 +59,7 @@ def loadFrom(self, filename): if line[0] in '#;\n': continue ref = line.strip() - if not ref in self.services: + if ref not in self.services: self.services.append(ref) cfg.close() @@ -91,20 +88,18 @@ def save(self): self.saveTo('/etc/epgimport/ignore.conf') def addService(self, ref): - if isinstance(ref, str): - if not ref in self.services: - self.services.append(ref) + if isinstance(ref, str) and ref not in self.services: + self.services.append(ref) def addServices(self, services): if isinstance(services, list): for s in services: - if not s in self.services: + if s not in self.services: self.services.append(s) def delService(self, ref): - if isinstance(ref, str): - if ref in self.services: - self.services.remove(ref) + if isinstance(ref, str) and ref in self.services: + self.services.remove(ref) def delAll(self): self.services = [] @@ -132,8 +127,8 @@ class filtersServicesSetup(Screen): MultiContentEntryText(pos = (50, 25), size = (380, 20), font = 1, flags = RT_HALIGN_LEFT, text = 1), MultiContentEntryText(pos = (100, 47), size = (400, 17), font = 2, flags = RT_HALIGN_LEFT, text = 2), ], - "fonts": [gFont("Regular", 21), gFont("Regular", 19), gFont("Regular", 16)], - "itemHeight": 65 + "fonts": [gFont("Regular", 21), gFont("Regular", 19), gFont("Regular", 16)], + "itemHeight": 65 } @@ -187,7 +182,7 @@ def addServiceCallback(self, *service): self.RefList.addServices(ref) else: refstr = ':'.join(ref.toString().split(':')[:11]) - if '1:0:' in refstr: + if any(x in refstr for x in ('1:0:', '4097:0:', '5001:0:', '5002:0:')): self.RefList.addService(refstr) self.updateList() self.updateButtons() @@ -268,7 +263,7 @@ def addAction(choice): if choice[1] == "providerlist": serviceHandler = eServiceCenter.getInstance() servicelist = serviceHandler.list(ref) - if not servicelist is None: + if servicelist is not None: providerlist = [] while True: service = servicelist.getNext() @@ -285,7 +280,7 @@ def addAction(choice): self.enterPath(ref) elif (ref.flags & 7) == 7: self.enterPath(ref) - elif not 'provider' in ref.toString() and not self.providers and not (ref.flags & (64 | 128)) and '%3a//' not in ref.toString(): + elif 'provider' not in ref.toString() and not self.providers and not (ref.flags & (64 | 128)) and '%3a//' not in ref.toString(): if ref.valid(): self.close(ref) diff --git a/src/EPGImport/gen_xmltv.py b/src/EPGImport/gen_xmltv.py index b6b87bc..679c405 100644 --- a/src/EPGImport/gen_xmltv.py +++ b/src/EPGImport/gen_xmltv.py @@ -1,35 +1,33 @@ -from __future__ import absolute_import -from __future__ import print_function from . import xmltvconverter date_format = '%Y%m%d%H%M%S' gen_categories = { -'Hobbies': (0x30, 0), -'Talk': (0x33, 0), -'GameShow': (0x31, 0), -'Reality': (0x34, 0), -'Animated': (0x55, 0), -'Comedy': (0x14, 0), -'Documentary': (0x23, 0), -'Educational': (0x90, 0), -'Film': (0x10, 0), -'Children': (0x50, 0), -'Arts/Culture': (0x70, 0), -'Crime/Mystery': (0x10, 85), -'Music': (0x60, 0), -'Nature': (0x91, 0), -'News': (0x20, 0), -'Unknown': (0x00, 0), -'Religion': (0x73, 0), -'Drama': (0x15, 0), -'Sports': (0x40, 0), -'Science/Nature': (0x90, 0), -'Adult': (0x18, 0) + 'Adult': (0x18, 0), + 'Animated': (0x55, 0), + 'Arts/Culture': (0x70, 0), + 'Children': (0x50, 0), + 'Comedy': (0x14, 0), + 'Crime/Mystery': (0x10, 85), + 'Documentary': (0x23, 0), + 'Drama': (0x15, 0), + 'Educational': (0x90, 0), + 'Film': (0x10, 0), + 'GameShow': (0x31, 0), + 'Hobbies': (0x30, 0), + 'Music': (0x60, 0), + 'Nature': (0x91, 0), + 'News': (0x20, 0), + 'Reality': (0x34, 0), + 'Religion': (0x73, 0), + 'Science/Nature': (0x90, 0), + 'Sports': (0x40, 0), + 'Talk': (0x33, 0), + 'Unknown': (0x00, 0) } def new(): - 'Factory method to return main class instance' + """Factory method to return main class instance""" return Gen_Xmltv() diff --git a/src/EPGImport/locale/ar.mo b/src/EPGImport/locale/ar.mo deleted file mode 100644 index 2cc59ec..0000000 Binary files a/src/EPGImport/locale/ar.mo and /dev/null differ diff --git a/src/EPGImport/locale/cs.mo b/src/EPGImport/locale/cs.mo deleted file mode 100644 index 81796a2..0000000 Binary files a/src/EPGImport/locale/cs.mo and /dev/null differ diff --git a/src/EPGImport/locale/cs.po b/src/EPGImport/locale/cs.po index 6a4937e..cf647ce 100644 --- a/src/EPGImport/locale/cs.po +++ b/src/EPGImport/locale/cs.po @@ -1,20 +1,24 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-04-01 17:07+0100\n" -"PO-Revision-Date: 2023-01-19 15:36+0100\n" +"POT-Creation-Date: 2023-01-16 06:40+0000\n" +"PO-Revision-Date: 2025-02-26 06:58+0100\n" "Last-Translator: Warder \n" "Language-Team: \n" "Language: cs\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: Poedit 3.2.2\n" -"X-Poedit-Basepath: .\n" +"X-Generator: Poedit 3.5\n" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid "" "\n" "Import of epg data will start.\n" @@ -26,89 +30,89 @@ msgstr "" "To může trvat několik minut.\n" "Je to v pořádku?" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid " events\n" msgstr " dění\n" -#: ../filtersServices.py:149 +#: ../filtersServices.py:153 msgid "Add Channel" msgstr "Přidat kanál" -#: ../filtersServices.py:148 +#: ../filtersServices.py:152 msgid "Add Provider" msgstr "Přidat poskytovatele" -#: ../filtersServices.py:260 +#: ../filtersServices.py:264 msgid "All services provider" msgstr "Všichni poskytovatelé služeb" -#: ../plugin.py:1099 -msgid "Automated EPG Import" -msgstr "Automatizovaný EPG Import" +#: ../plugin.py:1095 +msgid "Automated EPG Importer" +msgstr "Automatizovaný dovozce EPG" -#: ../plugin.py:368 +#: ../plugin.py:366 msgid "Automatic import EPG" msgstr "Automatický import EPG" -#: ../plugin.py:369 +#: ../plugin.py:367 msgid "Automatic start time" msgstr "Čas automatického spuštění" -#: ../plugin.py:311 ../plugin.py:559 ../plugin.py:639 +#: ../plugin.py:309 ../plugin.py:557 ../plugin.py:637 msgid "Cancel" msgstr "Zrušit" -#: ../plugin.py:373 +#: ../plugin.py:371 msgid "Choice days for start import" msgstr "Dny výběru pro zahájení importu" -#: ../plugin.py:695 +#: ../plugin.py:693 msgid "Clear" msgstr "Vymazat" -#: ../plugin.py:384 +#: ../plugin.py:382 msgid "Clearing current EPG before import" msgstr "Vymazání aktuálního EPG před importem" -#: ../plugin.py:377 +#: ../plugin.py:375 msgid "Consider setting \"Days Profile\"" msgstr "Zvažte nastavení \"Profil dnů\"" -#: ../plugin.py:652 +#: ../plugin.py:650 msgid "Days Profile" msgstr "Profil dnů" -#: ../filtersServices.py:227 +#: ../filtersServices.py:231 msgid "Delete all" msgstr "Smazat vše" -#: ../filtersServices.py:226 +#: ../filtersServices.py:230 msgid "Delete selected" msgstr "Odstranit vybrané soubory" -#: ../plugin.py:307 +#: ../plugin.py:305 msgid "EPG Import Configuration" msgstr "Konfigurace importu EPG" -#: ../plugin.py:720 +#: ../plugin.py:718 msgid "EPG Import Log" msgstr "Protokol importu EPG" -#: ../plugin.py:594 +#: ../plugin.py:592 msgid "EPG Import Sources" msgstr "Zdroje importu EPG" -#: ../plugin.py:794 +#: ../plugin.py:790 #, python-format msgid "EPG Import finished, %d events" msgstr "Import EPG dokončen, %d události" -#: ../plugin.py:1101 ../plugin.py:1102 ../plugin.py:1108 ../plugin.py:1113 -#: ../plugin.py:1118 ../plugin.py:1123 ../plugin.py:1131 ../plugin.py:1141 -msgid "EPG-Import" -msgstr "EPG-Import" +#: ../plugin.py:1097 ../plugin.py:1098 ../plugin.py:1104 ../plugin.py:1109 +#: ../plugin.py:1114 ../plugin.py:1119 ../plugin.py:1127 ../plugin.py:1137 +msgid "EPG-Importer" +msgstr "EPG-Importer" -#: ../plugin.py:485 +#: ../plugin.py:483 msgid "" "EPGImport\n" "Import of epg data is still in progress. Please wait." @@ -116,7 +120,7 @@ msgstr "" "EPGImport\n" "Import dat epg stále probíhá. Počkejte prosím." -#: ../plugin.py:500 +#: ../plugin.py:498 msgid "" "EPGImport\n" "Import of epg data will start.\n" @@ -128,7 +132,7 @@ msgstr "" "To může trvat několik minut.\n" "Je to v pořádku?" -#: ../plugin.py:509 +#: ../plugin.py:507 msgid "" "EPGImport Plugin\n" "Failed to start:\n" @@ -136,19 +140,19 @@ msgstr "" "EPGImport Plugin\n" "Nepodařilo se spustit:\n" -#: ../plugin.py:100 +#: ../plugin.py:105 msgid "Friday" msgstr "Pátek" -#: ../filtersServices.py:164 ../plugin.py:522 +#: ../filtersServices.py:168 ../plugin.py:520 msgid "Ignore services list" msgstr "Seznam ignorovaných služeb" -#: ../plugin.py:582 +#: ../plugin.py:580 msgid "Import current source" msgstr "Import aktuálního zdroje" -#: ../plugin.py:332 +#: ../plugin.py:330 #, python-format msgid "" "Importing: %s\n" @@ -157,129 +161,129 @@ msgstr "" "Import: %s\n" "%s události" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid "Last import: " msgstr "Poslední import: " -#: ../plugin.py:480 +#: ../plugin.py:478 #, python-format msgid "Last import: %s events" msgstr "Poslední import: %s události" -#: ../plugin.py:475 +#: ../plugin.py:473 #, python-format msgid "Last: %s %s, %d events" msgstr "Poslední cena: %s %s, %d události" -#: ../plugin.py:376 +#: ../plugin.py:374 msgid "Load EPG only for IPTV channels" msgstr "Načíst EPG pouze pro IPTV kanály" -#: ../plugin.py:375 +#: ../plugin.py:373 msgid "Load EPG only services in bouquets" msgstr "Načíst služby pouze EPG v kyticích" -#: ../plugin.py:382 +#: ../plugin.py:380 msgid "Load long descriptions up to X days" msgstr "Načtení dlouhých popisů až X dní" -#: ../plugin.py:313 +#: ../plugin.py:311 msgid "Manual" msgstr "Manuální" -#: ../plugin.py:96 +#: ../plugin.py:101 msgid "Monday" msgstr "Pondělí" -#: ../plugin.py:495 +#: ../plugin.py:493 msgid "No active EPG sources found, nothing to do" msgstr "Nebyly nalezeny žádné aktivní zdroje EPG, není co dělat" -#: ../plugin.py:83 +#: ../plugin.py:88 msgid "Press OK" msgstr "Stiskněte OK" -#: ../filtersServices.py:193 +#: ../filtersServices.py:197 msgid "Really delete all list?" msgstr "Opravdu smazat všechny seznamy?" -#: ../plugin.py:371 +#: ../plugin.py:369 msgid "Return to deep standby after import" msgstr "Návrat do hlubokého pohotovostního režimu po importu" -#: ../plugin.py:383 +#: ../plugin.py:381 msgid "Run AutoTimer after import" msgstr "Spuštění automatického časovače po importu" -#: ../plugin.py:101 +#: ../plugin.py:106 msgid "Saturday" msgstr "Sobota" -#: ../plugin.py:312 ../plugin.py:560 ../plugin.py:640 ../plugin.py:698 +#: ../plugin.py:310 ../plugin.py:558 ../plugin.py:638 ../plugin.py:696 msgid "Save" msgstr "Uložit" -#: ../filtersServices.py:279 ../plugin.py:523 +#: ../filtersServices.py:283 ../plugin.py:521 msgid "Select action" msgstr "Vyberte akci" -#: ../filtersServices.py:252 +#: ../filtersServices.py:256 msgid "Select service to add..." msgstr "Vyberte službu, kterou chcete přidat..." -#: ../plugin.py:379 +#: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" msgstr "Zobrazit \"EPGImport\" v rozšířeních" -#: ../plugin.py:380 +#: ../plugin.py:378 msgid "Show \"EPGImport\" in plugins" msgstr "Zobrazit \"EPGImport\" v pluginech" -#: ../plugin.py:522 +#: ../plugin.py:520 msgid "Show log" msgstr "Zobrazit log" -#: ../plugin.py:378 +#: ../plugin.py:376 msgid "Skip import on restart GUI" msgstr "Přeskočit import při restartování grafického uživatelského rozhraní" -#: ../plugin.py:314 +#: ../plugin.py:312 msgid "Sources" msgstr "Zdroje" -#: ../plugin.py:372 +#: ../plugin.py:370 msgid "Standby at startup" msgstr "Pohotovostní režim při spuštění" -#: ../plugin.py:374 +#: ../plugin.py:372 msgid "Start import after booting up" msgstr "Spustit import po spuštění" -#: ../plugin.py:102 +#: ../plugin.py:107 msgid "Sunday" msgstr "Neděle" -#: ../plugin.py:99 +#: ../plugin.py:104 msgid "Thursday" msgstr "Čtvrtek" -#: ../plugin.py:97 +#: ../plugin.py:102 msgid "Tuesday" msgstr "Úterý" -#: ../plugin.py:98 +#: ../plugin.py:103 msgid "Wednesday" msgstr "Středa" -#: ../plugin.py:370 +#: ../plugin.py:368 msgid "When in deep standby" msgstr "V hlubokém pohotovostním režimu" -#: ../plugin.py:726 +#: ../plugin.py:724 msgid "Write to /tmp/epgimport.log" msgstr "Napište do /tmp/epgimport.log" -#: ../plugin.py:662 +#: ../plugin.py:660 msgid "" "You may not use this settings!\n" "At least one day a week should be included!" @@ -287,7 +291,7 @@ msgstr "" "Toto nastavení nesmíte použít!\n" "Alespoň jeden den v týdnu by měl být zahrnut!" -#: ../plugin.py:794 +#: ../plugin.py:790 msgid "" "You must restart Enigma2 to load the EPG data,\n" "is this OK?" @@ -295,30 +299,30 @@ msgstr "" "Musíte restartovat Enigma2 pro načtení dat EPG,\n" "je to v pořádku?" -#: ../plugin.py:55 +#: ../plugin.py:60 msgid "always" msgstr "vždy" -#: ../plugin.py:58 +#: ../plugin.py:63 msgid "never" msgstr "nikdy" -#: ../plugin.py:57 +#: ../plugin.py:62 msgid "only automatic boot" msgstr "pouze automatické spouštění" -#: ../plugin.py:56 +#: ../plugin.py:61 msgid "only manual boot" msgstr "pouze ruční spuštění" -#: ../filtersServices.py:151 +#: ../filtersServices.py:155 msgid "press OK to save list" msgstr "stisknutím tlačítka OK uložte seznam" -#: ../plugin.py:72 +#: ../plugin.py:77 msgid "skip the import" -msgstr "Přeskočit import" +msgstr "přeskočit import" -#: ../plugin.py:71 +#: ../plugin.py:76 msgid "wake up and import" -msgstr "Probuzení a import" +msgstr "probuzení a import" diff --git a/src/EPGImport/locale/de.mo b/src/EPGImport/locale/de.mo deleted file mode 100644 index c3c09a1..0000000 Binary files a/src/EPGImport/locale/de.mo and /dev/null differ diff --git a/src/EPGImport/locale/de.po b/src/EPGImport/locale/de.po index afd773a..41a92c5 100755 --- a/src/EPGImport/locale/de.po +++ b/src/EPGImport/locale/de.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: plugins 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-16 06:40+0000\n" -"PO-Revision-Date: 2019-12-12 14:29+0100\n" +"PO-Revision-Date: 2025-02-26 17:59+0100\n" "Last-Translator: jbleyel\n" "Language-Team: none\n" "Language: de\n" @@ -16,7 +16,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: Poedit 2.2.4\n" +"X-Generator: Poedit 3.5\n" #: ../plugin.py:739 msgid "" @@ -229,16 +229,15 @@ msgstr "Aktion auswählen" #: ../filtersServices.py:256 msgid "Select service to add..." -msgstr "Wähle Sender zum Hinzufügen… " +msgstr "Wähle Sender zum Hinzufügen…" #: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" -msgstr "\"EPG-Import\" unter Erweiterungen anzeigen" +msgstr "\"EPG-Import\" in Erweiterungen anzeigen" #: ../plugin.py:378 -#, fuzzy msgid "Show \"EPGImport\" in plugins" -msgstr "\"EPG-Import\" unter Erweiterungen anzeigen" +msgstr "\"EPG-Import\" im PluginBrowser anzeigen" #: ../plugin.py:520 msgid "Show log" diff --git a/src/EPGImport/locale/el.mo b/src/EPGImport/locale/el.mo deleted file mode 100644 index ad68f43..0000000 Binary files a/src/EPGImport/locale/el.mo and /dev/null differ diff --git a/src/EPGImport/locale/en.mo b/src/EPGImport/locale/en.mo deleted file mode 100644 index f3c1f85..0000000 Binary files a/src/EPGImport/locale/en.mo and /dev/null differ diff --git a/src/EPGImport/locale/en_GB.mo b/src/EPGImport/locale/en_GB.mo deleted file mode 100644 index f1550da..0000000 Binary files a/src/EPGImport/locale/en_GB.mo and /dev/null differ diff --git a/src/EPGImport/locale/es.mo b/src/EPGImport/locale/es.mo deleted file mode 100644 index 756a469..0000000 Binary files a/src/EPGImport/locale/es.mo and /dev/null differ diff --git a/src/EPGImport/locale/et.mo b/src/EPGImport/locale/et.mo deleted file mode 100644 index 8d1507f..0000000 Binary files a/src/EPGImport/locale/et.mo and /dev/null differ diff --git a/src/EPGImport/locale/fi.mo b/src/EPGImport/locale/fi.mo deleted file mode 100644 index 7c121d1..0000000 Binary files a/src/EPGImport/locale/fi.mo and /dev/null differ diff --git a/src/EPGImport/locale/fr.mo b/src/EPGImport/locale/fr.mo deleted file mode 100644 index 8cdb55d..0000000 Binary files a/src/EPGImport/locale/fr.mo and /dev/null differ diff --git a/src/EPGImport/locale/hu.mo b/src/EPGImport/locale/hu.mo deleted file mode 100644 index a137489..0000000 Binary files a/src/EPGImport/locale/hu.mo and /dev/null differ diff --git a/src/EPGImport/locale/hu.po b/src/EPGImport/locale/hu.po index f67c8d0..6c0b8ff 100644 --- a/src/EPGImport/locale/hu.po +++ b/src/EPGImport/locale/hu.po @@ -1,22 +1,22 @@ -# English translations for plugins package. -# Copyright (C) 2012 THE plugins'S COPYRIGHT HOLDER -# This file is distributed under the same license as the plugins package. -# Automatically generated, 2012. +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. # +#, fuzzy msgid "" msgstr "" -"Project-Id-Version: EPGImport v1.3\n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-16 06:40+0000\n" -"PO-Revision-Date: 2018-10-26 18:55+0200\n" -"Last-Translator: Alec \n" -"Language-Team: alec \n" -"Language: hu_HU\n" +"PO-Revision-Date: 2025-02-26 07:00+0100\n" +"Last-Translator: Warder \n" +"Language-Team: \n" +"Language: 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: Poedit 2.2\n" +"X-Generator: Poedit 3.5\n" #: ../plugin.py:739 msgid "" @@ -28,7 +28,7 @@ msgstr "" "\n" "Az epg adatok importálása megkezdődik.\n" "Ez néhány percet vesz igénybe.\n" -"Rendben van ez így?" +"Kezdődhet az importálás?" #: ../plugin.py:739 msgid " events\n" @@ -36,7 +36,7 @@ msgstr " műsor információk\n" #: ../filtersServices.py:153 msgid "Add Channel" -msgstr "Csatorna hozzáadása" +msgstr "Csatorna hozzáadása lehetőségre" #: ../filtersServices.py:152 msgid "Add Provider" @@ -60,11 +60,11 @@ msgstr "Importálás kezdési ideje" #: ../plugin.py:309 ../plugin.py:557 ../plugin.py:637 msgid "Cancel" -msgstr "Mégse" +msgstr "Mégsem" #: ../plugin.py:371 msgid "Choice days for start import" -msgstr "Milyen napokon történjen az Importálás" +msgstr "Milyen napokon történjen az Import" #: ../plugin.py:693 msgid "Clear" @@ -72,7 +72,7 @@ msgstr "Törlés" #: ../plugin.py:382 msgid "Clearing current EPG before import" -msgstr "Az aktuális EPG törlése az importálás előtt" +msgstr "Az aktuális EPG törlése importálás előtt" #: ../plugin.py:375 msgid "Consider setting \"Days Profile\"" @@ -84,7 +84,7 @@ msgstr "Napok profil" #: ../filtersServices.py:231 msgid "Delete all" -msgstr "Az összes törlése" +msgstr "Összes törlése" #: ../filtersServices.py:230 msgid "Delete selected" @@ -92,7 +92,7 @@ msgstr "Kiválasztott törlése" #: ../plugin.py:305 msgid "EPG Import Configuration" -msgstr "EPGImport beállítás" +msgstr "EPG Import beállítás" #: ../plugin.py:718 msgid "EPG Import Log" @@ -150,7 +150,7 @@ msgstr "Csatornák listájának figyelmen kívül hagyása" #: ../plugin.py:580 msgid "Import current source" -msgstr "Kijelöltek importálása" +msgstr "Jelenlegi forrás importálása" #: ../plugin.py:330 #, python-format @@ -177,7 +177,7 @@ msgstr "Utolsó: %s %s, %d műsor információ" #: ../plugin.py:374 msgid "Load EPG only for IPTV channels" -msgstr "" +msgstr "EPG betöltése csak IPTV csatornákhoz" #: ../plugin.py:373 msgid "Load EPG only services in bouquets" @@ -189,7 +189,7 @@ msgstr "Részletes műsorleírást használó napok száma" #: ../plugin.py:311 msgid "Manual" -msgstr "Kézi importálás" +msgstr "Kézi" #: ../plugin.py:101 msgid "Monday" @@ -229,20 +229,19 @@ msgstr "Művelet kiválasztása" #: ../filtersServices.py:256 msgid "Select service to add..." -msgstr "Válasszon hozzáadandó csatornát..." +msgstr "Válassza ki a hozzáadni kívánt szolgáltatást..." #: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" msgstr "Az \"EPGImport\" megjelenítése a bővítményekben" #: ../plugin.py:378 -#, fuzzy msgid "Show \"EPGImport\" in plugins" -msgstr "Az \"EPGImport\" megjelenítése a bővítményekben" +msgstr "Az „EPGImport” megjelenítése a bővítményekben" #: ../plugin.py:520 msgid "Show log" -msgstr "Napló megjelenítése" +msgstr "Naplózás mutatása" #: ../plugin.py:376 msgid "Skip import on restart GUI" diff --git a/src/EPGImport/locale/it.mo b/src/EPGImport/locale/it.mo deleted file mode 100644 index bb68ec5..0000000 Binary files a/src/EPGImport/locale/it.mo and /dev/null differ diff --git a/src/EPGImport/locale/lt.mo b/src/EPGImport/locale/lt.mo deleted file mode 100644 index 1d03301..0000000 Binary files a/src/EPGImport/locale/lt.mo and /dev/null differ diff --git a/src/EPGImport/locale/nb.po b/src/EPGImport/locale/nb.po new file mode 100644 index 0000000..50c92d0 --- /dev/null +++ b/src/EPGImport/locale/nb.po @@ -0,0 +1,320 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-26 14:56+0100\n" +"PO-Revision-Date: 2025-02-26 14:56+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: nb_NO\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-SearchPath-0: .\n" + +#: filtersServices.py:149 +msgid "Add Provider" +msgstr "" + +#: filtersServices.py:150 +msgid "Add Channel" +msgstr "" + +#: filtersServices.py:152 +msgid "press OK to save list" +msgstr "" + +#: filtersServices.py:165 plugin.py:526 +msgid "Ignore services list" +msgstr "" + +#: filtersServices.py:194 +msgid "Really delete all list?" +msgstr "" + +#: filtersServices.py:227 +msgid "Delete selected" +msgstr "" + +#: filtersServices.py:228 +msgid "Delete all" +msgstr "" + +#: filtersServices.py:253 +msgid "Select service to add..." +msgstr "" + +#: filtersServices.py:261 +msgid "All services provider" +msgstr "" + +#: filtersServices.py:280 plugin.py:527 +msgid "Select action" +msgstr "" + +#: plugin.py:74 +msgid "always" +msgstr "" + +#: plugin.py:75 +msgid "only manual boot" +msgstr "" + +#: plugin.py:76 +msgid "only automatic boot" +msgstr "" + +#: plugin.py:77 +msgid "never" +msgstr "" + +#: plugin.py:87 +msgid "wake up and import" +msgstr "" + +#: plugin.py:88 +msgid "skip the import" +msgstr "" + +#: plugin.py:99 +msgid "Press OK" +msgstr "" + +#: plugin.py:108 +msgid "Monday" +msgstr "" + +#: plugin.py:109 +msgid "Tuesday" +msgstr "" + +#: plugin.py:110 +msgid "Wednesday" +msgstr "" + +#: plugin.py:111 +msgid "Thursday" +msgstr "" + +#: plugin.py:112 +msgid "Friday" +msgstr "" + +#: plugin.py:113 +msgid "Saturday" +msgstr "" + +#: plugin.py:114 +msgid "Sunday" +msgstr "" + +#: plugin.py:306 +msgid "EPG Import Configuration" +msgstr "" + +#: plugin.py:309 plugin.py:484 +#, python-format +msgid "Last import: %s events" +msgstr "" + +#: plugin.py:310 plugin.py:562 plugin.py:642 +msgid "Cancel" +msgstr "" + +#: plugin.py:311 plugin.py:563 plugin.py:643 plugin.py:701 +msgid "Save" +msgstr "" + +#: plugin.py:312 +msgid "Manual" +msgstr "" + +#: plugin.py:313 +msgid "Sources" +msgstr "" + +#: plugin.py:331 +#, python-format +msgid "" +"Importing:\n" +"%s %s events" +msgstr "" + +#: plugin.py:368 +msgid "Automatic import EPG" +msgstr "" + +#: plugin.py:369 +msgid "Automatic start time" +msgstr "" + +#: plugin.py:370 +msgid "When in deep standby" +msgstr "" + +#: plugin.py:371 +msgid "Return to deep standby after import" +msgstr "" + +#: plugin.py:372 +msgid "Standby at startup" +msgstr "" + +#: plugin.py:373 +msgid "Choice days for start import" +msgstr "" + +#: plugin.py:374 +msgid "Start import after booting up" +msgstr "" + +#: plugin.py:375 +msgid "Load EPG only services in bouquets" +msgstr "" + +#: plugin.py:376 +msgid "Load EPG only for IPTV channels" +msgstr "" + +#: plugin.py:377 +msgid "Consider setting \"Days Profile\"" +msgstr "" + +#: plugin.py:378 +msgid "Skip import on restart GUI" +msgstr "" + +#: plugin.py:379 +msgid "Show \"EPGImport\" in extensions" +msgstr "" + +#: plugin.py:380 +msgid "Show \"EPGImport\" in plugins" +msgstr "" + +#: plugin.py:382 +msgid "Load long descriptions up to X days" +msgstr "" + +#: plugin.py:383 +msgid "Run AutoTimer after import" +msgstr "" + +#: plugin.py:384 +msgid "Delete current EPG before import" +msgstr "" + +#: plugin.py:449 +msgid "Settings saved successfully !" +msgstr "" + +#: plugin.py:479 +#, python-format +msgid "Last: %s %s, %d events" +msgstr "" + +#: plugin.py:489 +msgid "" +"EPGImport\n" +"Import of epg data is still in progress. Please wait." +msgstr "" + +#: plugin.py:499 +msgid "No active EPG sources found, nothing to do" +msgstr "" + +#: plugin.py:504 +msgid "" +"EPGImport\n" +"Import of epg data will start.\n" +"This may take a few minutes.\n" +"Is this ok?" +msgstr "" + +#: plugin.py:513 +msgid "" +"EPGImport Plugin\n" +"Failed to start:\n" +msgstr "" + +#: plugin.py:526 +msgid "Show log" +msgstr "" + +#: plugin.py:564 +msgid "Import" +msgstr "" + +#: plugin.py:598 +msgid "EPG Import Sources" +msgstr "" + +#: plugin.py:655 +msgid "Days Profile" +msgstr "" + +#: plugin.py:661 +msgid "" +"You may not use this settings!\n" +"At least one day a week should be included!" +msgstr "" + +#: plugin.py:698 +msgid "Clear" +msgstr "" + +#: plugin.py:723 +msgid "EPG Import Log" +msgstr "" + +#: plugin.py:729 +msgid "Write to /tmp/epgimport.log" +msgstr "" + +#: plugin.py:745 +msgid "Last import: " +msgstr "" + +#: plugin.py:745 +msgid " events\n" +msgstr "" + +#: plugin.py:745 +msgid "" +"\n" +"Import of epg data will start.\n" +"This may take a few minutes.\n" +"Is this ok?" +msgstr "" + +#: plugin.py:793 +#, python-format +msgid "EPG Import finished, %d events" +msgstr "" + +#: plugin.py:793 +msgid "" +"You must restart Enigma2 to load the EPG data,\n" +"is this OK?" +msgstr "" + +#: plugin.py:1098 +msgid "Automated EPG Importer" +msgstr "" + +#: plugin.py:1100 +msgid "EPG-Importer Now" +msgstr "" + +#: plugin.py:1101 plugin.py:1107 plugin.py:1112 plugin.py:1117 plugin.py:1122 +#: plugin.py:1130 plugin.py:1140 +msgid "EPG-Importer" +msgstr "" diff --git a/src/EPGImport/locale/nl.mo b/src/EPGImport/locale/nl.mo deleted file mode 100644 index 7bbed92..0000000 Binary files a/src/EPGImport/locale/nl.mo and /dev/null differ diff --git a/src/EPGImport/locale/pl.mo b/src/EPGImport/locale/pl.mo deleted file mode 100644 index 8cba212..0000000 Binary files a/src/EPGImport/locale/pl.mo and /dev/null differ diff --git a/src/EPGImport/locale/pl.po b/src/EPGImport/locale/pl.po index e0f1355..b0f8457 100755 --- a/src/EPGImport/locale/pl.po +++ b/src/EPGImport/locale/pl.po @@ -6,11 +6,11 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: EPG-importer\n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-16 06:40+0000\n" -"PO-Revision-Date: 2025-01-24 20:37+0000\n" -"Last-Translator: ChatGpt \n" +"PO-Revision-Date: 2025-02-26 07:02+0100\n" +"Last-Translator: Warder \n" "Language-Team: \n" "Language: pl\n" "MIME-Version: 1.0\n" @@ -26,29 +26,29 @@ msgid "" "Is this ok?" msgstr "" "\n" -"Rozpocznie się import danych EPG.\n" -"To może potrwać kilka minut.\n" -"Czy to w porządku?" +"Import danych EPG rozpocznie się.\n" +"To może zająć chwile.\n" +"Czy jest ok?" #: ../plugin.py:739 msgid " events\n" -msgstr " wydarzeń\n" +msgstr " wydarzenia\n" #: ../filtersServices.py:153 msgid "Add Channel" -msgstr "Dodaj kanał" +msgstr "Dodaj Kanał" #: ../filtersServices.py:152 msgid "Add Provider" -msgstr "Dodaj dostawcę" +msgstr "Dodaj Nadawcę" #: ../filtersServices.py:264 msgid "All services provider" -msgstr "Wszyscy dostawcy usług" +msgstr "Wszystkie usługi" #: ../plugin.py:1095 msgid "Automated EPG Importer" -msgstr "Zautomatyzowany importer EPG" +msgstr "Automatyczny importer EPG" #: ../plugin.py:366 msgid "Automatic import EPG" @@ -64,23 +64,23 @@ msgstr "Anuluj" #: ../plugin.py:371 msgid "Choice days for start import" -msgstr "Wybierz dni dla rozpoczęcia importu" +msgstr "Wybór dni do rozpoczęcie importu" #: ../plugin.py:693 msgid "Clear" msgstr "Wyczyść" #: ../plugin.py:382 -msgid "Delete current EPG before import" -msgstr "Usuń bieżący EPG przed importem" +msgid "Clearing current EPG before import" +msgstr "Wyczyść obecne EPG przed importem" #: ../plugin.py:375 msgid "Consider setting \"Days Profile\"" -msgstr "Rozważ ustawienie \"Profilu dni\"" +msgstr "Rozważyć ustawienie \"dni profil\"" #: ../plugin.py:650 msgid "Days Profile" -msgstr "Profil dni" +msgstr "Profil" #: ../filtersServices.py:231 msgid "Delete all" @@ -88,7 +88,7 @@ msgstr "Usuń wszystko" #: ../filtersServices.py:230 msgid "Delete selected" -msgstr "Usuń zaznaczone" +msgstr "Usuń wybrane" #: ../plugin.py:305 msgid "EPG Import Configuration" @@ -105,12 +105,12 @@ msgstr "Źródła importu EPG" #: ../plugin.py:790 #, python-format msgid "EPG Import finished, %d events" -msgstr "Import EPG zakończony, %d wydarzeń" +msgstr "Zakończono importowanie EPG, %d zdarzeń" #: ../plugin.py:1097 ../plugin.py:1098 ../plugin.py:1104 ../plugin.py:1109 #: ../plugin.py:1114 ../plugin.py:1119 ../plugin.py:1127 ../plugin.py:1137 msgid "EPG-Importer" -msgstr "Importer EPG" +msgstr "EPG-Import" #: ../plugin.py:483 msgid "" @@ -118,7 +118,7 @@ msgid "" "Import of epg data is still in progress. Please wait." msgstr "" "EPGImport\n" -"Import danych EPG nadal trwa. Proszę czekać." +"Import danych EPG jest nadal w toku. Proszę czekać." #: ../plugin.py:498 msgid "" @@ -128,8 +128,8 @@ msgid "" "Is this ok?" msgstr "" "EPGImport\n" -"Import danych EPG rozpocznie się.\n" -"To może potrwać kilka minut.\n" +"Import danych EPG zostanie uruchomiony.\n" +"Może to potrwać kilka minut.\n" "Czy to w porządku?" #: ../plugin.py:507 @@ -137,8 +137,8 @@ msgid "" "EPGImport Plugin\n" "Failed to start:\n" msgstr "" -"Wtyczka EPGImport\n" -"Nie udało się uruchomić:\n" +"Wtyczka EPGImport \n" +"Nie udało się uruchomić\n" #: ../plugin.py:105 msgid "Friday" @@ -146,7 +146,7 @@ msgstr "Piątek" #: ../filtersServices.py:168 ../plugin.py:520 msgid "Ignore services list" -msgstr "Ignoruj listę usług" +msgstr "Lista usług ignorowanych" #: ../plugin.py:580 msgid "Import current source" @@ -158,8 +158,8 @@ msgid "" "Importing: %s\n" "%s events" msgstr "" -"Importowanie: %s\n" -"%s wydarzeń" +"Importowanie: %s\\n \n" +"%s zdarzeń" #: ../plugin.py:739 msgid "Last import: " @@ -168,24 +168,24 @@ msgstr "Ostatni import: " #: ../plugin.py:478 #, python-format msgid "Last import: %s events" -msgstr "Ostatni import: %s wydarzeń" +msgstr "Ostatni import: %s zdarzeń" #: ../plugin.py:473 #, python-format msgid "Last: %s %s, %d events" -msgstr "Ostatni: %s %s, %d wydarzeń" +msgstr "Ostatnie: %s %s, %d zdarzenia" #: ../plugin.py:374 msgid "Load EPG only for IPTV channels" -msgstr "Ładuj EPG tylko dla kanałów IPTV" +msgstr "Załaduj EPG tylko dla kanałów IPTV" #: ../plugin.py:373 msgid "Load EPG only services in bouquets" -msgstr "Ładuj EPG tylko dla usług w listach kanałów" +msgstr "Załaduj usługi EPG tylko w bukietach" #: ../plugin.py:380 msgid "Load long descriptions up to X days" -msgstr "Ładuj długie opisy do X dni" +msgstr "Załaduj długie opisy do X dni" #: ../plugin.py:311 msgid "Manual" @@ -197,23 +197,23 @@ msgstr "Poniedziałek" #: ../plugin.py:493 msgid "No active EPG sources found, nothing to do" -msgstr "Nie znaleziono aktywnych źródeł EPG, nic do zrobienia" +msgstr "Nie znaleziono aktywnych źródeł EPG, nic do roboty" #: ../plugin.py:88 msgid "Press OK" -msgstr "Naciśnij OK" +msgstr "Wybierz" #: ../filtersServices.py:197 msgid "Really delete all list?" -msgstr "Naprawdę chcesz usunąć całą listę?" +msgstr "Naprawdę usunąć całą listę?" #: ../plugin.py:369 msgid "Return to deep standby after import" -msgstr "Powrót do głębokiego stanu uśpienia po imporcie" +msgstr "Powrót do głębokiego czuwania po imporcie" #: ../plugin.py:381 msgid "Run AutoTimer after import" -msgstr "Uruchom AutoTimer po imporcie" +msgstr "Uruchom AutoTimer po zaimportowaniu" #: ../plugin.py:106 msgid "Saturday" @@ -229,15 +229,15 @@ msgstr "Wybierz akcję" #: ../filtersServices.py:256 msgid "Select service to add..." -msgstr "Wybierz usługę do dodania..." +msgstr "Wybierz usługę, którą chcesz dodać..." #: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" -msgstr "Pokaż \"EPGImport\" w rozszerzeniach" +msgstr "Pokaż „EPGImport” w rozszerzeniach" #: ../plugin.py:378 msgid "Show \"EPGImport\" in plugins" -msgstr "Pokaż \"EPGImport\" we wtyczkach" +msgstr "Pokaż „EPGImport” w wtyczkach" #: ../plugin.py:520 msgid "Show log" @@ -253,11 +253,11 @@ msgstr "Źródła" #: ../plugin.py:370 msgid "Standby at startup" -msgstr "Czuwanie przy uruchamianiu" +msgstr "Czuwanie przy starcie" #: ../plugin.py:372 msgid "Start import after booting up" -msgstr "Rozpocznij import po uruchomieniu dekodera" +msgstr "Rozpocznij import po uruchomieniu" #: ../plugin.py:107 msgid "Sunday" @@ -277,11 +277,11 @@ msgstr "Środa" #: ../plugin.py:368 msgid "When in deep standby" -msgstr "Gdy w głębokim stanie uśpienia" +msgstr "W głębokim stanie gotowości" #: ../plugin.py:724 msgid "Write to /tmp/epgimport.log" -msgstr "Zapisz do /tmp/epgimport.log" +msgstr "Zapisz w /tmp/epgimport.log" #: ../plugin.py:660 msgid "" @@ -289,7 +289,7 @@ msgid "" "At least one day a week should be included!" msgstr "" "Nie możesz używać tych ustawień!\n" -"Co najmniej jeden dzień w tygodniu powinien być uwzględniony!" +"Należy uwzględnić co najmniej jeden dzień w tygodniu!" #: ../plugin.py:790 msgid "" @@ -297,7 +297,7 @@ msgid "" "is this OK?" msgstr "" "Musisz ponownie uruchomić Enigma2, aby załadować dane EPG,\n" -"Czy to w porządku?" +"czy to w porządku?" #: ../plugin.py:60 msgid "always" @@ -309,11 +309,11 @@ msgstr "nigdy" #: ../plugin.py:62 msgid "only automatic boot" -msgstr "tylko automatyczne uruchomienie" +msgstr "tylko automatyczny rozruch" #: ../plugin.py:61 msgid "only manual boot" -msgstr "tylko ręczne uruchomienie" +msgstr "tylko ręczny rozruch" #: ../filtersServices.py:155 msgid "press OK to save list" @@ -321,8 +321,8 @@ msgstr "naciśnij OK, aby zapisać listę" #: ../plugin.py:77 msgid "skip the import" -msgstr "pomiń import" +msgstr "nie importuj" #: ../plugin.py:76 msgid "wake up and import" -msgstr "obudź i zaimportuj" +msgstr "uruchom i importuj" diff --git a/src/EPGImport/locale/pt.mo b/src/EPGImport/locale/pt.mo deleted file mode 100644 index b099ae0..0000000 Binary files a/src/EPGImport/locale/pt.mo and /dev/null differ diff --git a/src/EPGImport/locale/ru.mo b/src/EPGImport/locale/ru.mo deleted file mode 100644 index f32b96d..0000000 Binary files a/src/EPGImport/locale/ru.mo and /dev/null differ diff --git a/src/EPGImport/locale/ru.po b/src/EPGImport/locale/ru.po index d07c564..d8789ba 100755 --- a/src/EPGImport/locale/ru.po +++ b/src/EPGImport/locale/ru.po @@ -1,18 +1,22 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-16 06:40+0000\n" -"PO-Revision-Date: 2020-07-14 22:58+0300\n" -"Last-Translator: \n" -"Language-Team: LANGUAGE \n" -"Language: ru_RU\n" +"PO-Revision-Date: 2025-02-26 07:03+0100\n" +"Last-Translator: Warder \n" +"Language-Team: \n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-SourceCharset: UTF-8\n" -"X-Generator: Poedit 2.3.1\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.5\n" #: ../plugin.py:739 msgid "" @@ -22,29 +26,29 @@ msgid "" "Is this ok?" msgstr "" "\n" -"Начинаем импорт данных EPG.\n" +"Начнётся импорт epg данных.\n" "Это может занять несколько минут.\n" -"Вас устраивает?" +"Можем продолжить?" #: ../plugin.py:739 msgid " events\n" -msgstr " событий\n" +msgstr " события\n" #: ../filtersServices.py:153 msgid "Add Channel" -msgstr "Добавить канал" +msgstr "Добавить Канал" #: ../filtersServices.py:152 msgid "Add Provider" -msgstr "Добавить провайдер" +msgstr "Добавить Оператора" #: ../filtersServices.py:264 msgid "All services provider" -msgstr "Все сервисы провайдера" +msgstr "Все сервисы оператора" #: ../plugin.py:1095 msgid "Automated EPG Importer" -msgstr "Автоматический импортер EPG" +msgstr "Автоматический EPG Импортер" #: ../plugin.py:366 msgid "Automatic import EPG" @@ -52,7 +56,7 @@ msgstr "Автоматический импорт EPG" #: ../plugin.py:367 msgid "Automatic start time" -msgstr "Начало автоматического старта" +msgstr "Время автозапуска" #: ../plugin.py:309 ../plugin.py:557 ../plugin.py:637 msgid "Cancel" @@ -60,23 +64,23 @@ msgstr "Отмена" #: ../plugin.py:371 msgid "Choice days for start import" -msgstr "Выбор дней для старта импорта" +msgstr "Выбор дней для начала импорта" #: ../plugin.py:693 msgid "Clear" -msgstr "Очистка" +msgstr "Очистить" #: ../plugin.py:382 msgid "Clearing current EPG before import" -msgstr "Очистка текущего EPG перед импортом" +msgstr "Очистить текущий EPG перед импортом" #: ../plugin.py:375 msgid "Consider setting \"Days Profile\"" -msgstr "Учесть настройки \"Профиль дней\"" +msgstr "Учесть настройки \"Профиль Дней\"" #: ../plugin.py:650 msgid "Days Profile" -msgstr "Профиль дней" +msgstr "Профиль Дней" #: ../filtersServices.py:231 msgid "Delete all" @@ -88,33 +92,33 @@ msgstr "Удалить выбранное" #: ../plugin.py:305 msgid "EPG Import Configuration" -msgstr "Импортер EPG - Настройки" +msgstr "EPG Импорт Конфигурация" #: ../plugin.py:718 msgid "EPG Import Log" -msgstr "Импортер EPG - Журнал" +msgstr "EPG Импорт Журнал" #: ../plugin.py:592 msgid "EPG Import Sources" -msgstr "Источники импорта" +msgstr "EPG Импорт Источники" #: ../plugin.py:790 #, python-format msgid "EPG Import finished, %d events" -msgstr "Импорт EPG закончен, %d событий" +msgstr "EPG Импорт завершён, %d событий" #: ../plugin.py:1097 ../plugin.py:1098 ../plugin.py:1104 ../plugin.py:1109 #: ../plugin.py:1114 ../plugin.py:1119 ../plugin.py:1127 ../plugin.py:1137 msgid "EPG-Importer" -msgstr "Импортер EPG" +msgstr "EPG-Импортер" #: ../plugin.py:483 msgid "" "EPGImport\n" "Import of epg data is still in progress. Please wait." msgstr "" -"Импортер EPG\n" -"Импорт данных EPG все еще продолжается. Пожалуйста, подождите." +"EPGИмпорт\n" +"Импорт epg данных всё ещё продолжается. Пожалуйста, подождите." #: ../plugin.py:498 msgid "" @@ -123,18 +127,18 @@ msgid "" "This may take a few minutes.\n" "Is this ok?" msgstr "" -"Импортер EPG\n" -"Начинаем импорт данных EPG.\n" +"EPGИмпорт\n" +"Начнётся импорт epg данных.\n" "Это может занять несколько минут.\n" -"Вас устраивает?" +"Согласны с этим?" #: ../plugin.py:507 msgid "" "EPGImport Plugin\n" "Failed to start:\n" msgstr "" -"Импортер EPG\n" -"Ошибка старта:\n" +"EPGИмпорт Плагин\n" +"Сбой запуска:\n" #: ../plugin.py:105 msgid "Friday" @@ -142,7 +146,7 @@ msgstr "Пятница" #: ../filtersServices.py:168 ../plugin.py:520 msgid "Ignore services list" -msgstr "Список игногируемых сервисов" +msgstr "Список игнор-сервисов" #: ../plugin.py:580 msgid "Import current source" @@ -154,38 +158,38 @@ msgid "" "Importing: %s\n" "%s events" msgstr "" -"Импортируем: %s\n" -"%s событий" +"Импорт: %s\n" +"%s события" #: ../plugin.py:739 msgid "Last import: " -msgstr "Последний импорт: " +msgstr "Прошлый импорт: " #: ../plugin.py:478 #, python-format msgid "Last import: %s events" -msgstr "Последний импорт: %s событий" +msgstr "Последний импорт: %s события" #: ../plugin.py:473 #, python-format msgid "Last: %s %s, %d events" -msgstr "Последний импорт: %s %s, %d событий" +msgstr "Последнее: %s %s, %d событий" #: ../plugin.py:374 msgid "Load EPG only for IPTV channels" -msgstr "Загрузка EPG только для IPTV" +msgstr "Загрузить EPG только для каналов IPTV" #: ../plugin.py:373 msgid "Load EPG only services in bouquets" -msgstr "Загрузка EPG только для сервисов из букетов" +msgstr "Загружать EPG только в букеты" #: ../plugin.py:380 msgid "Load long descriptions up to X days" -msgstr "Загрузка длинных описаний не больше чем на Х дней" +msgstr "Загружать длинные описания до X дней" #: ../plugin.py:311 msgid "Manual" -msgstr "Вручную" +msgstr "Руководство" #: ../plugin.py:101 msgid "Monday" @@ -193,23 +197,23 @@ msgstr "Понедельник" #: ../plugin.py:493 msgid "No active EPG sources found, nothing to do" -msgstr "Активные источники EPG не найдены." +msgstr "Активных источников EPG не найдено, делать нечего" #: ../plugin.py:88 msgid "Press OK" -msgstr "Нажмите ОК" +msgstr "Нажмите OK" #: ../filtersServices.py:197 msgid "Really delete all list?" -msgstr "Удалить весь список?" +msgstr "Удаляем весь список?" #: ../plugin.py:369 msgid "Return to deep standby after import" -msgstr "Возврат в режим глубокого ожидания после импорта" +msgstr "Возврат в режим глубокого сна после импорта" #: ../plugin.py:381 msgid "Run AutoTimer after import" -msgstr "По окончании импорта запустить Автотаймер" +msgstr "Запуск АвтоТаймера после импорта" #: ../plugin.py:106 msgid "Saturday" @@ -221,19 +225,19 @@ msgstr "Сохранить" #: ../filtersServices.py:283 ../plugin.py:521 msgid "Select action" -msgstr "Выбор действия" +msgstr "Выбрать действие" #: ../filtersServices.py:256 msgid "Select service to add..." -msgstr "Выберите канал для добавления..." +msgstr "Выберите сервис для добавления..." #: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" -msgstr "Показать \"Импортер EPG\" в меню дополнений" +msgstr "Показать \"EPGИмпорт\" в расширениях" #: ../plugin.py:378 msgid "Show \"EPGImport\" in plugins" -msgstr "Показать \"Импортер EPG\" в списке плагинов" +msgstr "Показать «EPGImport» в плагинах" #: ../plugin.py:520 msgid "Show log" @@ -241,7 +245,7 @@ msgstr "Показать журнал" #: ../plugin.py:376 msgid "Skip import on restart GUI" -msgstr "Пропускать импорт после рестарта GUI" +msgstr "Пропустить импорт при перезапуске GUI" #: ../plugin.py:312 msgid "Sources" @@ -249,11 +253,11 @@ msgstr "Источники" #: ../plugin.py:370 msgid "Standby at startup" -msgstr "После загрузки, перейти в режим ожидания" +msgstr "Ожидание при запуске" #: ../plugin.py:372 msgid "Start import after booting up" -msgstr "Старт импорта после загрузки" +msgstr "Начать импорт после загрузки" #: ../plugin.py:107 msgid "Sunday" @@ -273,7 +277,7 @@ msgstr "Среда" #: ../plugin.py:368 msgid "When in deep standby" -msgstr "Поведение в глубоком ожидании" +msgstr "Когда в режиме глубокого сна" #: ../plugin.py:724 msgid "Write to /tmp/epgimport.log" @@ -285,15 +289,15 @@ msgid "" "At least one day a week should be included!" msgstr "" "Вы не можете использовать эти настройки!\n" -"По крайней мере один день недели должен быть активирован!" +"По крайней мере, один день в неделю должен быть включен!" #: ../plugin.py:790 msgid "" "You must restart Enigma2 to load the EPG data,\n" "is this OK?" msgstr "" -"Для загрузки данных EPG требуется перезагрузка,\n" -"Вас устраивает?" +"Необходимо перезапустить Enigma2, чтобы загрузить данные EPG,\n" +"согласны с ЭТИМ?" #: ../plugin.py:60 msgid "always" @@ -305,20 +309,20 @@ msgstr "никогда" #: ../plugin.py:62 msgid "only automatic boot" -msgstr "только при авто загрузке" +msgstr "только автоматическая загрузка" #: ../plugin.py:61 msgid "only manual boot" -msgstr "только при ручной загрузке" +msgstr "только ручная загрузка" #: ../filtersServices.py:155 msgid "press OK to save list" -msgstr "Нажмите ОК для сохранения списка" +msgstr "нажмите OK для сохранения списка" #: ../plugin.py:77 msgid "skip the import" -msgstr "пропускать импорт" +msgstr "пропустить импорт" #: ../plugin.py:76 msgid "wake up and import" -msgstr "разбудить для импорта" +msgstr "пробуждение и импорт" diff --git a/src/EPGImport/locale/sk.mo b/src/EPGImport/locale/sk.mo deleted file mode 100644 index f994614..0000000 Binary files a/src/EPGImport/locale/sk.mo and /dev/null differ diff --git a/src/EPGImport/locale/sk.po b/src/EPGImport/locale/sk.po index c4f5e3d..2ba5216 100644 --- a/src/EPGImport/locale/sk.po +++ b/src/EPGImport/locale/sk.po @@ -1,22 +1,24 @@ -# Massimo Pissarello , 2022. +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy msgid "" msgstr "" -"Project-Id-Version: epgimport italian translation\n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-04-01 17:07+0100\n" -"PO-Revision-Date: 2023-01-19 15:32+0100\n" +"POT-Creation-Date: 2023-01-16 06:40+0000\n" +"PO-Revision-Date: 2025-02-26 07:04+0100\n" "Last-Translator: Warder \n" -"Language-Team: Italian <>\n" +"Language-Team: \n" "Language: 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: Poedit 3.2.2\n" -"X-Poedit-Basepath: .\n" -"X-Poedit-SourceCharset: UTF-8\n" +"X-Generator: Poedit 3.5\n" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid "" "\n" "Import of epg data will start.\n" @@ -28,89 +30,89 @@ msgstr "" "Môže to trvať niekoľko minút.\n" "Je to v poriadku?" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid " events\n" msgstr " Udalosti\n" -#: ../filtersServices.py:149 +#: ../filtersServices.py:153 msgid "Add Channel" msgstr "Pridať kanál" -#: ../filtersServices.py:148 +#: ../filtersServices.py:152 msgid "Add Provider" msgstr "Pridať poskytovateľa" -#: ../filtersServices.py:260 +#: ../filtersServices.py:264 msgid "All services provider" msgstr "Všetci poskytovatelia služieb" -#: ../plugin.py:1099 -msgid "Automated EPG Import" -msgstr "Automatizovaný EPG Import" +#: ../plugin.py:1095 +msgid "Automated EPG Importer" +msgstr "Automatizovaný stahovač EPG" -#: ../plugin.py:368 +#: ../plugin.py:366 msgid "Automatic import EPG" msgstr "Automatický import EPG" -#: ../plugin.py:369 +#: ../plugin.py:367 msgid "Automatic start time" msgstr "Čas automatického spustenia" -#: ../plugin.py:311 ../plugin.py:559 ../plugin.py:639 +#: ../plugin.py:309 ../plugin.py:557 ../plugin.py:637 msgid "Cancel" msgstr "Zrušiť" -#: ../plugin.py:373 +#: ../plugin.py:371 msgid "Choice days for start import" msgstr "Výberové dni pre spustenie importu" -#: ../plugin.py:695 +#: ../plugin.py:693 msgid "Clear" msgstr "Vyčistiť" -#: ../plugin.py:384 +#: ../plugin.py:382 msgid "Clearing current EPG before import" msgstr "Zúčtovanie aktuálneho EPG pred importom" -#: ../plugin.py:377 +#: ../plugin.py:375 msgid "Consider setting \"Days Profile\"" msgstr "Zvážte nastavenie \"Profilu dní\"" -#: ../plugin.py:652 +#: ../plugin.py:650 msgid "Days Profile" msgstr "Profil dní" -#: ../filtersServices.py:227 +#: ../filtersServices.py:231 msgid "Delete all" msgstr "Zmazať všetko" -#: ../filtersServices.py:226 +#: ../filtersServices.py:230 msgid "Delete selected" msgstr "Vymazať vybrané" -#: ../plugin.py:307 +#: ../plugin.py:305 msgid "EPG Import Configuration" msgstr "Konfigurácia importu EPG" -#: ../plugin.py:720 +#: ../plugin.py:718 msgid "EPG Import Log" msgstr "Denník importu EPG" -#: ../plugin.py:594 +#: ../plugin.py:592 msgid "EPG Import Sources" msgstr "Zdroje importu EPG" -#: ../plugin.py:794 +#: ../plugin.py:790 #, python-format msgid "EPG Import finished, %d events" msgstr "Epg Import dokončený, %d udalosti" -#: ../plugin.py:1101 ../plugin.py:1102 ../plugin.py:1108 ../plugin.py:1113 -#: ../plugin.py:1118 ../plugin.py:1123 ../plugin.py:1131 ../plugin.py:1141 -msgid "EPG-Import" -msgstr "EPG-Import" +#: ../plugin.py:1097 ../plugin.py:1098 ../plugin.py:1104 ../plugin.py:1109 +#: ../plugin.py:1114 ../plugin.py:1119 ../plugin.py:1127 ../plugin.py:1137 +msgid "EPG-Importer" +msgstr "EPG-dovozca" -#: ../plugin.py:485 +#: ../plugin.py:483 msgid "" "EPGImport\n" "Import of epg data is still in progress. Please wait." @@ -118,7 +120,7 @@ msgstr "" "EPGImport\n" "Import epg dát stále prebieha. Prosím počkajte." -#: ../plugin.py:500 +#: ../plugin.py:498 msgid "" "EPGImport\n" "Import of epg data will start.\n" @@ -130,7 +132,7 @@ msgstr "" "Môže to trvať niekoľko minút.\n" "Je to v poriadku?" -#: ../plugin.py:509 +#: ../plugin.py:507 msgid "" "EPGImport Plugin\n" "Failed to start:\n" @@ -138,19 +140,19 @@ msgstr "" "Doplnok EPGImport\n" "Nepodarilo sa spustiť:\n" -#: ../plugin.py:100 +#: ../plugin.py:105 msgid "Friday" msgstr "Piatok" -#: ../filtersServices.py:164 ../plugin.py:522 +#: ../filtersServices.py:168 ../plugin.py:520 msgid "Ignore services list" msgstr "Ignorovať zoznam služieb" -#: ../plugin.py:582 +#: ../plugin.py:580 msgid "Import current source" msgstr "Import aktuálneho zdroja" -#: ../plugin.py:332 +#: ../plugin.py:330 #, python-format msgid "" "Importing: %s\n" @@ -159,129 +161,129 @@ msgstr "" "Import: %s\n" "%s podujatia" -#: ../plugin.py:743 +#: ../plugin.py:739 msgid "Last import: " msgstr "Posledný import: " -#: ../plugin.py:480 +#: ../plugin.py:478 #, python-format msgid "Last import: %s events" msgstr "Posledný import: %s udalosti" -#: ../plugin.py:475 +#: ../plugin.py:473 #, python-format msgid "Last: %s %s, %d events" msgstr "Posledný: %s %s, %d udalosti" -#: ../plugin.py:376 +#: ../plugin.py:374 msgid "Load EPG only for IPTV channels" msgstr "Načítajte EPG iba pre kanály IPTV" -#: ../plugin.py:375 +#: ../plugin.py:373 msgid "Load EPG only services in bouquets" msgstr "Načítajte služby EPG iba v kyticiach" -#: ../plugin.py:382 +#: ../plugin.py:380 msgid "Load long descriptions up to X days" msgstr "Načítanie dlhých popisov až na X dní" -#: ../plugin.py:313 +#: ../plugin.py:311 msgid "Manual" msgstr "Manuálna" -#: ../plugin.py:96 +#: ../plugin.py:101 msgid "Monday" msgstr "Pondelok" -#: ../plugin.py:495 +#: ../plugin.py:493 msgid "No active EPG sources found, nothing to do" msgstr "Nenašli sa žiadne aktívne zdroje EPG, nič nerobiť" -#: ../plugin.py:83 +#: ../plugin.py:88 msgid "Press OK" msgstr "Stlač OK" -#: ../filtersServices.py:193 +#: ../filtersServices.py:197 msgid "Really delete all list?" msgstr "Naozaj odstrániť celý zoznam?" -#: ../plugin.py:371 +#: ../plugin.py:369 msgid "Return to deep standby after import" msgstr "Po importe sa vráťte do hlbokého pohotovostného režimu" -#: ../plugin.py:383 +#: ../plugin.py:381 msgid "Run AutoTimer after import" msgstr "Spustenie nástroja AutoTimer po importe" -#: ../plugin.py:101 +#: ../plugin.py:106 msgid "Saturday" msgstr "Sobota" -#: ../plugin.py:312 ../plugin.py:560 ../plugin.py:640 ../plugin.py:698 +#: ../plugin.py:310 ../plugin.py:558 ../plugin.py:638 ../plugin.py:696 msgid "Save" msgstr "Uložiť" -#: ../filtersServices.py:279 ../plugin.py:523 +#: ../filtersServices.py:283 ../plugin.py:521 msgid "Select action" msgstr "Vybrať akciu" -#: ../filtersServices.py:252 +#: ../filtersServices.py:256 msgid "Select service to add..." msgstr "Vyberte položku Služba, ktorá sa má pridať..." -#: ../plugin.py:379 +#: ../plugin.py:377 msgid "Show \"EPGImport\" in extensions" msgstr "Zobraziť \"EPGImport\" v rozšíreniach" -#: ../plugin.py:380 +#: ../plugin.py:378 msgid "Show \"EPGImport\" in plugins" msgstr "Zobraziť \"EPGImport\" v pluginoch" -#: ../plugin.py:522 +#: ../plugin.py:520 msgid "Show log" msgstr "Zobraziť záznam" -#: ../plugin.py:378 +#: ../plugin.py:376 msgid "Skip import on restart GUI" msgstr "Preskočiť import pri reštartovaní grafického používateľského rozhrania" -#: ../plugin.py:314 +#: ../plugin.py:312 msgid "Sources" msgstr "Zdrojov" -#: ../plugin.py:372 +#: ../plugin.py:370 msgid "Standby at startup" msgstr "Pohotovostný režim pri spustení" -#: ../plugin.py:374 +#: ../plugin.py:372 msgid "Start import after booting up" msgstr "Spustenie importu po spustení" -#: ../plugin.py:102 +#: ../plugin.py:107 msgid "Sunday" msgstr "Nedeľa" -#: ../plugin.py:99 +#: ../plugin.py:104 msgid "Thursday" msgstr "Štvrtok" -#: ../plugin.py:97 +#: ../plugin.py:102 msgid "Tuesday" msgstr "Utorok" -#: ../plugin.py:98 +#: ../plugin.py:103 msgid "Wednesday" msgstr "Streda" -#: ../plugin.py:370 +#: ../plugin.py:368 msgid "When in deep standby" msgstr "V hlbokom pohotovostnom režime" -#: ../plugin.py:726 +#: ../plugin.py:724 msgid "Write to /tmp/epgimport.log" msgstr "Napíšte na /tmp/epgimport.log" -#: ../plugin.py:662 +#: ../plugin.py:660 msgid "" "You may not use this settings!\n" "At least one day a week should be included!" @@ -289,7 +291,7 @@ msgstr "" "Tieto nastavenia nesmiete použiť!\n" "Mal by byť zahrnutý aspoň jeden deň v týždni!" -#: ../plugin.py:794 +#: ../plugin.py:790 msgid "" "You must restart Enigma2 to load the EPG data,\n" "is this OK?" @@ -297,30 +299,30 @@ msgstr "" "Ak chcete načítať údaje EPG, musíte reštartovať Enigma2,\n" "je to v poriadku?" -#: ../plugin.py:55 +#: ../plugin.py:60 msgid "always" msgstr "vždy" -#: ../plugin.py:58 +#: ../plugin.py:63 msgid "never" msgstr "nikdy" -#: ../plugin.py:57 +#: ../plugin.py:62 msgid "only automatic boot" msgstr "iba automatické spustenie" -#: ../plugin.py:56 +#: ../plugin.py:61 msgid "only manual boot" msgstr "iba manuálne spustenie" -#: ../filtersServices.py:151 +#: ../filtersServices.py:155 msgid "press OK to save list" msgstr "stlačením tlačidla OK uložte zoznam" -#: ../plugin.py:72 +#: ../plugin.py:77 msgid "skip the import" msgstr "preskočiť import" -#: ../plugin.py:71 +#: ../plugin.py:76 msgid "wake up and import" msgstr "prebudenie a import" diff --git a/src/EPGImport/locale/sv.po b/src/EPGImport/locale/sv.po new file mode 100644 index 0000000..f4dc3f2 --- /dev/null +++ b/src/EPGImport/locale/sv.po @@ -0,0 +1,378 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-26 15:00+0100\n" +"PO-Revision-Date: 2025-02-26 15:00+0100\n" +"Last-Translator: Warder \n" +"Language-Team: \n" +"Language: sv_FI\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-SearchPath-0: .\n" + +#: filtersServices.py:149 +msgid "Add Provider" +msgstr "Pridať poskytovateľa" + +#: filtersServices.py:150 +msgid "Add Channel" +msgstr "Pridať kanál" + +#: filtersServices.py:152 +msgid "press OK to save list" +msgstr "stlačením tlačidla OK uložte zoznam" + +#: filtersServices.py:165 plugin.py:526 +msgid "Ignore services list" +msgstr "Ignorovať zoznam služieb" + +#: filtersServices.py:194 +msgid "Really delete all list?" +msgstr "Naozaj odstrániť celý zoznam?" + +#: filtersServices.py:227 +msgid "Delete selected" +msgstr "Vymazať vybrané" + +#: filtersServices.py:228 +msgid "Delete all" +msgstr "Zmazať všetko" + +#: filtersServices.py:253 +msgid "Select service to add..." +msgstr "Vyberte položku Služba, ktorá sa má pridať..." + +#: filtersServices.py:261 +msgid "All services provider" +msgstr "Všetci poskytovatelia služieb" + +#: filtersServices.py:280 plugin.py:527 +msgid "Select action" +msgstr "Vybrať akciu" + +#: plugin.py:74 +msgid "always" +msgstr "vždy" + +#: plugin.py:75 +msgid "only manual boot" +msgstr "iba manuálne spustenie" + +#: plugin.py:76 +msgid "only automatic boot" +msgstr "iba automatické spustenie" + +#: plugin.py:77 +msgid "never" +msgstr "nikdy" + +#: plugin.py:87 +msgid "wake up and import" +msgstr "prebudenie a import" + +#: plugin.py:88 +msgid "skip the import" +msgstr "preskočiť import" + +#: plugin.py:99 +msgid "Press OK" +msgstr "Stlač OK" + +#: plugin.py:108 +msgid "Monday" +msgstr "Pondelok" + +#: plugin.py:109 +msgid "Tuesday" +msgstr "Utorok" + +#: plugin.py:110 +msgid "Wednesday" +msgstr "Streda" + +#: plugin.py:111 +msgid "Thursday" +msgstr "Štvrtok" + +#: plugin.py:112 +msgid "Friday" +msgstr "Piatok" + +#: plugin.py:113 +msgid "Saturday" +msgstr "Sobota" + +#: plugin.py:114 +msgid "Sunday" +msgstr "Nedeľa" + +#: plugin.py:306 +msgid "EPG Import Configuration" +msgstr "Konfigurácia importu EPG" + +#: plugin.py:309 plugin.py:484 +#, python-format +msgid "Last import: %s events" +msgstr "Posledný import: %s udalosti" + +#: plugin.py:310 plugin.py:562 plugin.py:642 +msgid "Cancel" +msgstr "Zrušiť" + +#: plugin.py:311 plugin.py:563 plugin.py:643 plugin.py:701 +msgid "Save" +msgstr "Uložiť" + +#: plugin.py:312 +msgid "Manual" +msgstr "Manuálna" + +#: plugin.py:313 +msgid "Sources" +msgstr "Zdrojov" + +#: plugin.py:331 +#, fuzzy, python-format +#| msgid "" +#| "Importing: %s\n" +#| "%s events" +msgid "" +"Importing:\n" +"%s %s events" +msgstr "" +"Import: %s\n" +"%s podujatia" + +#: plugin.py:368 +msgid "Automatic import EPG" +msgstr "Automatický import EPG" + +#: plugin.py:369 +msgid "Automatic start time" +msgstr "Čas automatického spustenia" + +#: plugin.py:370 +msgid "When in deep standby" +msgstr "V hlbokom pohotovostnom režime" + +#: plugin.py:371 +msgid "Return to deep standby after import" +msgstr "Po importe sa vráťte do hlbokého pohotovostného režimu" + +#: plugin.py:372 +msgid "Standby at startup" +msgstr "Pohotovostný režim pri spustení" + +#: plugin.py:373 +msgid "Choice days for start import" +msgstr "Výberové dni pre spustenie importu" + +#: plugin.py:374 +msgid "Start import after booting up" +msgstr "Spustenie importu po spustení" + +#: plugin.py:375 +msgid "Load EPG only services in bouquets" +msgstr "Načítajte služby EPG iba v kyticiach" + +#: plugin.py:376 +#, fuzzy +#| msgid "Load EPG only for IPTV channels" +msgid "Load EPG only for IPTV channels" +msgstr "Načítajte EPG iba pre kanály IPTV" + +#: plugin.py:377 +msgid "Consider setting \"Days Profile\"" +msgstr "Zvážte nastavenie \"Profilu dní\"" + +#: plugin.py:378 +msgid "Skip import on restart GUI" +msgstr "Preskočiť import pri reštartovaní grafického používateľského rozhrania" + +#: plugin.py:379 +#, fuzzy +#| msgid "Show \"EPGImport\" in extensions" +msgid "Show \"EPGImport\" in extensions" +msgstr "Zobraziť \"EPGImport\" v rozšíreniach" + +#: plugin.py:380 +#, fuzzy +#| msgid "Show \"EPGImport\" in plugins" +msgid "Show \"EPGImport\" in plugins" +msgstr "Zobraziť \"EPGImport\" v pluginoch" + +#: plugin.py:382 +msgid "Load long descriptions up to X days" +msgstr "Načítanie dlhých popisov až na X dní" + +#: plugin.py:383 +msgid "Run AutoTimer after import" +msgstr "Spustenie nástroja AutoTimer po importe" + +#: plugin.py:384 +#, fuzzy +#| msgid "Clearing current EPG before import" +msgid "Delete current EPG before import" +msgstr "Zúčtovanie aktuálneho EPG pred importom" + +#: plugin.py:449 +msgid "Settings saved successfully !" +msgstr "" + +#: plugin.py:479 +#, python-format +msgid "Last: %s %s, %d events" +msgstr "Posledný: %s %s, %d udalosti" + +#: plugin.py:489 +msgid "" +"EPGImport\n" +"Import of epg data is still in progress. Please wait." +msgstr "" +"EPGImport\n" +"Import epg dát stále prebieha. Prosím počkajte." + +#: plugin.py:499 +msgid "No active EPG sources found, nothing to do" +msgstr "Nenašli sa žiadne aktívne zdroje EPG, nič nerobiť" + +#: plugin.py:504 +msgid "" +"EPGImport\n" +"Import of epg data will start.\n" +"This may take a few minutes.\n" +"Is this ok?" +msgstr "" +"EPGImport\n" +"Spustí sa import údajov EPG.\n" +"Môže to trvať niekoľko minút.\n" +"Je to v poriadku?" + +#: plugin.py:513 +msgid "" +"EPGImport Plugin\n" +"Failed to start:\n" +msgstr "" +"Doplnok EPGImport\n" +"Nepodarilo sa spustiť:\n" + +#: plugin.py:526 +msgid "Show log" +msgstr "Zobraziť záznam" + +#: plugin.py:564 +#, fuzzy +#| msgid "EPG-Importer" +msgid "Import" +msgstr "EPG-dovozca" + +#: plugin.py:598 +msgid "EPG Import Sources" +msgstr "Zdroje importu EPG" + +#: plugin.py:655 +msgid "Days Profile" +msgstr "Profil dní" + +#: plugin.py:661 +msgid "" +"You may not use this settings!\n" +"At least one day a week should be included!" +msgstr "" +"Tieto nastavenia nesmiete použiť!\n" +"Mal by byť zahrnutý aspoň jeden deň v týždni!" + +#: plugin.py:698 +msgid "Clear" +msgstr "Vyčistiť" + +#: plugin.py:723 +msgid "EPG Import Log" +msgstr "Denník importu EPG" + +#: plugin.py:729 +msgid "Write to /tmp/epgimport.log" +msgstr "Napíšte na /tmp/epgimport.log" + +#: plugin.py:745 +msgid "Last import: " +msgstr "Posledný import: " + +#: plugin.py:745 +msgid " events\n" +msgstr " Udalosti\n" + +#: plugin.py:745 +msgid "" +"\n" +"Import of epg data will start.\n" +"This may take a few minutes.\n" +"Is this ok?" +msgstr "" +"\n" +"Spustí sa import epg dát.\n" +"Môže to trvať niekoľko minút.\n" +"Je to v poriadku?" + +#: plugin.py:793 +#, python-format +msgid "EPG Import finished, %d events" +msgstr "Epg Import dokončený, %d udalosti" + +#: plugin.py:793 +msgid "" +"You must restart Enigma2 to load the EPG data,\n" +"is this OK?" +msgstr "" +"Ak chcete načítať údaje EPG, musíte reštartovať Enigma2,\n" +"je to v poriadku?" + +#: plugin.py:1098 +msgid "Automated EPG Importer" +msgstr "Automatizovaný stahovač EPG" + +#: plugin.py:1100 +#, fuzzy +#| msgid "EPG-Importer" +msgid "EPG-Importer Now" +msgstr "EPG-dovozca" + +#: plugin.py:1101 plugin.py:1107 plugin.py:1112 plugin.py:1117 plugin.py:1122 +#: plugin.py:1130 plugin.py:1140 +#, fuzzy +#| msgid "EPG-Importer" +msgid "EPG-Importer" +msgstr "EPG-dovozca" + +#, fuzzy +#~| msgid "Add Channel" +#~ msgid "all channels" +#~ msgstr "Pridať kanál" + +#, fuzzy +#~| msgid "Start import after booting up" +#~ msgid "Start import after standby" +#~ msgstr "Spustenie importu po spustení" + +#, fuzzy +#~| msgid "Start import after booting up" +#~ msgid "Start import after resuming from standby mode." +#~ msgstr "Spustenie importu po spustení" + +#, fuzzy +#~| msgid "EPG Import Log" +#~ msgid "EPG Import" +#~ msgstr "Denník importu EPG" + +#~ msgid "Import current source" +#~ msgstr "Import aktuálneho zdroja" diff --git a/src/EPGImport/locale/tr.mo b/src/EPGImport/locale/tr.mo deleted file mode 100644 index 0555314..0000000 Binary files a/src/EPGImport/locale/tr.mo and /dev/null differ diff --git a/src/EPGImport/locale/uk.mo b/src/EPGImport/locale/uk.mo deleted file mode 100644 index b73854b..0000000 Binary files a/src/EPGImport/locale/uk.mo and /dev/null differ diff --git a/src/EPGImport/plugin.py b/src/EPGImport/plugin.py index 61ca6f1..9524184 100644 --- a/src/EPGImport/plugin.py +++ b/src/EPGImport/plugin.py @@ -1,3 +1,9 @@ +from os import remove +from os.path import exists +from time import localtime, mktime, strftime, strptime, time, asctime + +from enigma import eServiceCenter, eServiceReference, eEPGCache, eTimer, getDesktop + # for localized messages from . import _ from . import log @@ -8,46 +14,37 @@ from . import EPGConfig -import os -import time - -import enigma - try: - from Components.SystemInfo import BoxInfo - IMAGEDISTRO = BoxInfo.getItem("distro") + from Components.SystemInfo import BoxInfo + IMAGEDISTRO = BoxInfo.getItem("distro") except: - from boxbranding import getImageDistro - IMAGEDISTRO = getImageDistro() + from boxbranding import getImageDistro + IMAGEDISTRO = getImageDistro() # Config from Components.ActionMap import ActionMap from Components.Button import Button -from Components.config import config, configfile, ConfigEnableDisable, ConfigSubsection, ConfigYesNo, ConfigClock, getConfigListEntry, ConfigText, ConfigSelection, ConfigNumber, ConfigSubDict, NoSave +from Components.config import config, ConfigEnableDisable, ConfigSubsection, ConfigYesNo, ConfigClock, getConfigListEntry, ConfigText, ConfigSelection, ConfigNumber, ConfigSubDict, NoSave from Components.ConfigList import ConfigListScreen from Components.Label import Label import Components.PluginComponent from Components.ScrollLabel import ScrollLabel -from Components.Sources.StaticText import StaticText from Plugins.Plugin import PluginDescriptor from Screens.ChoiceBox import ChoiceBox from Screens.MessageBox import MessageBox from Screens.Screen import Screen import Screens.Standby from Tools import Notifications -from Tools.Directories import fileExists +from Tools.Directories import fileExists, SCOPE_PLUGINS, resolveFilename, isPluginInstalled from Tools.FuzzyDate import FuzzyTime -try: - from Tools.StbHardware import getFPWasTimerWakeup -except: - from Tools.DreamboxHardware import getFPWasTimerWakeup +from Tools.StbHardware import getFPWasTimerWakeup import NavigationInstance def lastMACbyte(): try: - return int(open('/sys/class/net/eth0/address').readline().strip()[-2:], 16) + return int(open("/sys/class/net/eth0/address").readline().strip()[-2:], 16) except: return 256 @@ -61,15 +58,25 @@ def calcDefaultStarttime(): return (5 * 60 * 60) + offset -#Set default configuration +# historically located (not a problem, we want to update it) +CONFIG_PATH = "/etc/epgimport" +STANDBY_FLAG_FILE = "/tmp/enigmastandby" + +# Global variable +autoStartTimer = None +_session = None +BouquetChannelListList = None +serviceIgnoreList = None + +# Set default configuration config.plugins.epgimport = ConfigSubsection() config.plugins.epgimport.enabled = ConfigEnableDisable(default=False) config.plugins.epgimport.runboot = ConfigSelection(default="4", choices=[ - ("1", _("always")), - ("2", _("only manual boot")), - ("3", _("only automatic boot")), - ("4", _("never")) - ]) + ("1", _("always")), + ("2", _("only manual boot")), + ("3", _("only automatic boot")), + ("4", _("never")) +]) config.plugins.epgimport.runboot_restart = ConfigYesNo(default=False) config.plugins.epgimport.runboot_day = ConfigYesNo(default=False) config.plugins.epgimport.wakeupsleep = ConfigEnableDisable(default=False) @@ -78,13 +85,13 @@ def calcDefaultStarttime(): config.plugins.epgimport.showinplugins = ConfigYesNo(default=IMAGEDISTRO != "openatv") config.plugins.epgimport.showinextensions = ConfigYesNo(default=True) config.plugins.epgimport.deepstandby = ConfigSelection(default="skip", choices=[ - ("wakeup", _("wake up and import")), - ("skip", _("skip the import")) - ]) + ("wakeup", _("wake up and import")), + ("skip", _("skip the import")) +]) config.plugins.epgimport.standby_afterwakeup = ConfigYesNo(default=False) config.plugins.epgimport.shutdown = ConfigYesNo(default=False) config.plugins.epgimport.longDescDays = ConfigNumber(default=5) -#config.plugins.epgimport.showinmainmenu = ConfigYesNo(default = False) +# config.plugins.epgimport.showinmainmenu = ConfigYesNo(default=False) config.plugins.epgimport.deepstandby_afterimport = NoSave(ConfigYesNo(default=False)) config.plugins.epgimport.parse_autotimer = ConfigYesNo(default=False) config.plugins.epgimport.import_onlybouquet = ConfigYesNo(default=False) @@ -92,7 +99,7 @@ def calcDefaultStarttime(): config.plugins.epgimport.clear_oldepg = ConfigYesNo(default=False) config.plugins.epgimport.day_profile = ConfigSelection(choices=[("1", _("Press OK"))], default="1") config.plugins.extra_epgimport = ConfigSubsection() -config.plugins.extra_epgimport.last_import = ConfigText(default="none") +config.plugins.extra_epgimport.last_import = ConfigText(default="0") config.plugins.extra_epgimport.day_import = ConfigSubDict() for i in range(7): @@ -106,48 +113,39 @@ def calcDefaultStarttime(): _("Friday"), _("Saturday"), _("Sunday"), - ] - -# historically located (not a problem, we want to update it) -CONFIG_PATH = '/etc/epgimport' - -# Global variable -autoStartTimer = None -_session = None -BouquetChannelListList = None -serviceIgnoreList = None +] def getAlternatives(service): if not service: return None - alternativeServices = enigma.eServiceCenter.getInstance().list(service) + alternativeServices = eServiceCenter.getInstance().list(service) return alternativeServices and alternativeServices.getContent("S", True) def getRefNum(ref): - ref = ref.split(':')[3:7] - try: - return int(ref[0], 16) << 48 | int(ref[1], 16) << 32 | int(ref[2], 16) << 16 | int(ref[3], 16) >> 16 - except: - return + ref = ref.split(":")[3:7] + try: + return int(ref[0], 16) << 48 | int(ref[1], 16) << 32 | int(ref[2], 16) << 16 | int(ref[3], 16) >> 16 + except: + return def getBouquetChannelList(): channels = [] - serviceHandler = enigma.eServiceCenter.getInstance() - mask = (enigma.eServiceReference.isMarker | enigma.eServiceReference.isDirectory) - altrernative = enigma.eServiceReference.isGroup + serviceHandler = eServiceCenter.getInstance() + mask = (eServiceReference.isMarker | eServiceReference.isDirectory) + altrernative = eServiceReference.isGroup if config.usage.multibouquet.value: bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet' - bouquet_root = enigma.eServiceReference(bouquet_rootstr) + bouquet_root = eServiceReference(bouquet_rootstr) list = serviceHandler.list(bouquet_root) if list: while True: s = list.getNext() if not s.valid(): break - if s.flags & enigma.eServiceReference.isDirectory: + if s.flags & eServiceReference.isDirectory: info = serviceHandler.info(s) if info: clist = serviceHandler.list(s) @@ -170,9 +168,9 @@ def getBouquetChannelList(): channels.append(refnum) else: bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet' - bouquet_root = enigma.eServiceReference(bouquet_rootstr) + bouquet_root = eServiceReference(bouquet_rootstr) services = serviceHandler.list(bouquet_root) - if not services is None: + if services is not None: while True: service = services.getNext() if not service.valid(): @@ -200,7 +198,7 @@ def channelFilter(ref): # ignore non IPTV if config.plugins.epgimport.import_onlyiptv.value and ("%3a//" not in ref.lower() or ref.startswith("1")): return False - sref = enigma.eServiceReference(ref) + sref = eServiceReference(ref) refnum = getRefNum(sref.toString()) if config.plugins.epgimport.import_onlybouquet.value: global BouquetChannelListList @@ -213,10 +211,10 @@ def channelFilter(ref): if serviceIgnoreList is None: serviceIgnoreList = [getRefNum(x) for x in filtersServices.filtersServicesList.servicesList()] if refnum in serviceIgnoreList: - print("Serviceref is in ignore list:", sref.toString(), file=log) + print(f"Serviceref is in ignore list:{sref.toString()}", file=log) return False if "%3a//" in ref.lower(): - # print>>log, "URL detected in serviceref, not checking fake recording on serviceref:", ref + # print("URL detected in serviceref, not checking fake recording on serviceref:", ref, file=log) return True fakeRecService = NavigationInstance.instance.recordService(sref, True) if fakeRecService: @@ -224,36 +222,40 @@ def channelFilter(ref): NavigationInstance.instance.stopRecordService(fakeRecService) # -7 (errNoSourceFound) occurs when tuner is disconnected. r = fakeRecResult in (0, -7) - #if not r: - # print>>log, "Rejected (%d): %s" % (fakeRecResult, ref) return r - print("Invalid serviceref string:", ref, file=log) + print(f"Invalid serviceref string: {ref}", file=log) return False -epgimport = EPGImport.EPGImport(enigma.eEPGCache.getInstance(), channelFilter) +try: + epgcache_instance = eEPGCache.getInstance() + if not epgcache_instance: + print("[EPGImport] Failed to get valid EPGCache instance.", file=log) + else: + print("[EPGImport] EPGCache instance obtained successfully.", file=log) + epgimport = EPGImport.EPGImport(epgcache_instance, channelFilter) +except Exception as e: + print(f"[EPGImport] Error obtaining EPGCache instance: {e}", file=log) lastImportResult = None def startImport(): - EPGImport.HDD_EPG_DAT = config.misc.epgcache_filename.value - if config.plugins.epgimport.clear_oldepg.value and hasattr(epgimport.epgcache, 'flushEPG'): - EPGImport.unlink_if_exists(EPGImport.HDD_EPG_DAT) - EPGImport.unlink_if_exists(EPGImport.HDD_EPG_DAT + '.backup') - epgimport.epgcache.flushEPG() - epgimport.onDone = doneImport - epgimport.beginImport(longDescUntil=config.plugins.epgimport.longDescDays.value * 24 * 3600 + time.time()) + if not epgimport.isImportRunning(): + EPGImport.HDD_EPG_DAT = config.misc.epgcache_filename.value + if config.plugins.epgimport.clear_oldepg.value and hasattr(epgimport.epgcache, "flushEPG"): + EPGImport.unlink_if_exists(EPGImport.HDD_EPG_DAT) + EPGImport.unlink_if_exists(f"{EPGImport.HDD_EPG_DAT}.backup") + epgimport.epgcache.flushEPG() + epgimport.onDone = doneImport + epgimport.beginImport(longDescUntil=config.plugins.epgimport.longDescDays.value * 24 * 3600 + time()) + else: + print("[startImport] Already running, won't start again") ################################## # Configuration GUI -HD = False -try: - if enigma.getDesktop(0).size().width() >= 1280: - HD = True -except: - pass +HD = True if getDesktop(0).size().width() >= 1280 else False class EPGImportConfig(ConfigListScreen, Screen): @@ -301,12 +303,11 @@ class EPGImportConfig(ConfigListScreen, Screen): """ def __init__(self, session, args=0): - self.session = session self.skin = EPGImportConfig.skin self.setup_title = _("EPG Import Configuration") Screen.__init__(self, session) self["status"] = Label() - self["statusbar"] = Label() + self["statusbar"] = Label(_("Last import: %s events") % config.plugins.extra_epgimport.last_import.value) self["key_red"] = Button(_("Cancel")) self["key_green"] = Button(_("Save")) self["key_yellow"] = Button(_("Manual")) @@ -328,8 +329,8 @@ def __init__(self, session, args=0): self.prev_onlybouquet = config.plugins.epgimport.import_onlybouquet.value self.initConfig() self.createSetup() - self.importStatusTemplate = _("Importing: %s\n%s events") - self.updateTimer = enigma.eTimer() + self.importStatusTemplate = _("Importing:\n%s %s events") + self.updateTimer = eTimer() self.updateTimer.callback.append(self.updateStatus) self.updateTimer.start(2000) self.updateStatus() @@ -362,6 +363,7 @@ def getPrevValues(section): else: res[key] = val.value return res + self.EPG = config.plugins.epgimport self.prev_values = getPrevValues(self.EPG) self.cfg_enabled = getConfigListEntry(_("Automatic import EPG"), self.EPG.enabled) @@ -377,7 +379,7 @@ def getPrevValues(section): self.cfg_runboot_restart = getConfigListEntry(_("Skip import on restart GUI"), self.EPG.runboot_restart) self.cfg_showinextensions = getConfigListEntry(_("Show \"EPGImport\" in extensions"), self.EPG.showinextensions) self.cfg_showinplugins = getConfigListEntry(_("Show \"EPGImport\" in plugins"), self.EPG.showinplugins) -# self.cfg_showinmainmenu = getConfigListEntry(_("Show \"EPG Importer\" in main menu"), self.EPG.showinmainmenu) +# self.cfg_showinmainmenu = getConfigListEntry(_("Show \"EPG Importer\" in main menu"), self.EPG.showinmainmenu) self.cfg_longDescDays = getConfigListEntry(_("Load long descriptions up to X days"), self.EPG.longDescDays) self.cfg_parse_autotimer = getConfigListEntry(_("Run AutoTimer after import"), self.EPG.parse_autotimer) self.cfg_clear_oldepg = getConfigListEntry(_("Delete current EPG before import"), config.plugins.epgimport.clear_oldepg) @@ -391,25 +393,24 @@ def createSetup(self): list.append(self.cfg_shutdown) if not self.EPG.shutdown.value: list.append(self.cfg_standby_afterwakeup) - list.append(self.cfg_day_profile) + list.append(self.cfg_day_profile) list.append(self.cfg_runboot) if self.EPG.runboot.value != "4": list.append(self.cfg_runboot_day) if self.EPG.runboot.value == "1" or self.EPG.runboot.value == "2": list.append(self.cfg_runboot_restart) - list.append(self.cfg_showinextensions) - list.append(self.cfg_showinplugins) list.append(self.cfg_import_onlybouquet) list.append(self.cfg_import_onlyiptv) - if hasattr(enigma.eEPGCache, 'flushEPG'): + if hasattr(eEPGCache, "flushEPG"): list.append(self.cfg_clear_oldepg) list.append(self.cfg_longDescDays) - if fileExists("/usr/lib/enigma2/python/Plugins/Extensions/AutoTimer/plugin.py"): + if isPluginInstalled("AutoTimer"): try: - from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer list.append(self.cfg_parse_autotimer) except: print("[XMLTVImport] AutoTimer Plugin not installed", file=log) + list.append(self.cfg_showinextensions) + list.append(self.cfg_showinplugins) self["config"].list = list self["config"].l.setList(list) @@ -419,6 +420,7 @@ def newConfig(self): self.createSetup() def keyRed(self): + def setPrevValues(section, values): for (key, val) in section.content.items.items(): value = values.get(key, None) @@ -432,13 +434,20 @@ def setPrevValues(section, values): def keyGreen(self): self.updateTimer.stop() - if not fileExists("/usr/lib/enigma2/python/Plugins/Extensions/AutoTimer/plugin.py") and self.EPG.parse_autotimer.value: + if self.EPG.parse_autotimer.value and not isPluginInstalled("AutoTimer"): self.EPG.parse_autotimer.value = False if self.EPG.shutdown.value: self.EPG.standby_afterwakeup.value = False - self.EPG.save() if self.prev_onlybouquet != config.plugins.epgimport.import_onlybouquet.value or (autoStartTimer is not None and autoStartTimer.prev_multibouquet != config.usage.multibouquet.value): EPGConfig.channelCache = {} + self.save() + + def save(self): + if self["config"].isChanged(): + for x in self["config"].list: + x[1].save() + self.EPG.save() + self.session.open(MessageBox, _("Settings saved successfully !"), MessageBox.TYPE_INFO, timeout=5) self.close(True, self.session) def keyLeft(self): @@ -450,7 +459,7 @@ def keyRight(self): self.newConfig() def keyOk(self): - ConfigListScreen.keyOK(self) + # ConfigListScreen.keyOK(self) sel = self["config"].getCurrent()[1] if sel and sel == self.EPG.day_profile: self.session.open(EPGImportProfile) @@ -463,17 +472,34 @@ def updateStatus(self): self["status"].setText(text) if lastImportResult and (lastImportResult != self.lastImportResult): start, count = lastImportResult + """ + ## issue crash trhead + # try: + # d, t = FuzzyTime(start, inPast=True) + # except: + # # Not all images have inPast + # d, t = FuzzyTime(start) + """ try: - d, t = FuzzyTime(start, inPast=True) - except: - # Not all images have inPast - d, t = FuzzyTime(start) - self["statusbar"].setText(_("Last: %s %s, %d events") % (d, t, count)) - self.lastImportResult = lastImportResult + if isinstance(start, str): + start = mktime(strptime(start, "%Y-%m-%d %H:%M:%S")) + elif not isinstance(start, (int, float)): + raise ValueError("Start value is not a valid timestamp or string") + + d, t = FuzzyTime(int(start), inPast=True) + except Exception as e: + print(f"[EPGImport] Error FuzzyTime: {e}") + try: + d, t = FuzzyTime(int(start)) + except Exception as e: + print(f"[EPGImport] Fallback with FuzzyTime also failed: {e}") + + self["statusbar"].setText(_(f"Last import: {d} {t}, {count} events")) + self.lastImportResult = lastImportResult def keyInfo(self): last_import = config.plugins.extra_epgimport.last_import.value - self.session.open(MessageBox, _("Last import: %s events") % (last_import), type=MessageBox.TYPE_INFO) + self.session.open(MessageBox, _("Last import: %s events") % last_import, type=MessageBox.TYPE_INFO) def doimport(self, one_source=None): if epgimport.isImportRunning(): @@ -550,17 +576,17 @@ class EPGImportSources(Screen): """ def __init__(self, session): - self.session = session Screen.__init__(self, session) self["key_red"] = Button(_("Cancel")) self["key_green"] = Button(_("Save")) + self["key_yellow"] = Button(_("Import")) self["key_blue"] = Button() cfg = EPGConfig.loadUserSettings() filter = cfg["sources"] tree = [] cat = None for x in EPGConfig.enumSources(CONFIG_PATH, filter=None, categories=True): - if hasattr(x, 'description'): + if hasattr(x, "description"): sel = (filter is None) or (x.description in filter) entry = (x.description, x.description, sel) if cat is None: @@ -575,9 +601,9 @@ def __init__(self, session): tree.append(cat) self["list"] = ExpandableSelectionList.ExpandableSelectionList(tree, enableWrapAround=True) if tree: - self["key_yellow"] = Button(_("Import current source")) + self["key_yellow"].show() else: - self["key_yellow"] = Button() + self["key_yellow"].hide() self["setupActions"] = ActionMap(["SetupActions", "ColorActions"], { "red": self.cancel, @@ -590,9 +616,9 @@ def __init__(self, session): self.setTitle(_("EPG Import Sources")) def save(self): - # Make the entries unique through a set + """ Make the entries unique through a set """ sources = list(set([item[1] for item in self["list"].enumSelected()])) - print("[XMLTVImport] Selected sources:", sources, file=log) + print(f"[XMLTVImport] Selected sources:{sources}", file=log) EPGConfig.storeUserSettings(sources=sources) self.close(True, sources, None) @@ -607,9 +633,9 @@ def do_import(self): item = self["list"].list[idx][0] source = [item[1] or ""] cfg = {"sources": source} - print("[XMLTVImport] Selected source: ", source, file=log) + print(f"[XMLTVImport] Selected source: {source}", file=log) except Exception as e: - print("[XMLTVImport] Error at selected source:", e, file=log) + print(f"[XMLTVImport] Error at selected source:{e}", file=log) else: if cfg["sources"] != "": self.close(False, None, cfg) @@ -626,7 +652,6 @@ class EPGImportProfile(ConfigListScreen, Screen): """ def __init__(self, session, args=0): - self.session = session Screen.__init__(self, session) self.list = [] for i in range(7): @@ -648,15 +673,14 @@ def setCustomTitle(self): self.setTitle(_("Days Profile")) def save(self): - if not config.plugins.extra_epgimport.day_import[0].value: - if not config.plugins.extra_epgimport.day_import[1].value: - if not config.plugins.extra_epgimport.day_import[2].value: - if not config.plugins.extra_epgimport.day_import[3].value: - if not config.plugins.extra_epgimport.day_import[4].value: - if not config.plugins.extra_epgimport.day_import[5].value: - if not config.plugins.extra_epgimport.day_import[6].value: - self.session.open(MessageBox, _("You may not use this settings!\nAt least one day a week should be included!"), MessageBox.TYPE_INFO, timeout=6) - return + if all(not config.plugins.extra_epgimport.day_import[i].value for i in range(7)): + self.session.open( + MessageBox, + _("You may not use this settings!\nAt least one day a week should be included!"), + MessageBox.TYPE_INFO, + timeout=6 + ) + return for x in self["config"].list: x[1].save() self.close() @@ -688,11 +712,12 @@ class EPGImportLog(Screen): def __init__(self, session): self.session = session Screen.__init__(self, session) + self.log = log self["key_red"] = Button(_("Clear")) self["key_green"] = Button() self["key_yellow"] = Button() self["key_blue"] = Button(_("Save")) - self["list"] = ScrollLabel(log.getvalue()) + self["list"] = ScrollLabel(self.log.getvalue()) self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ColorActions", "MenuActions"], { "red": self.clear, @@ -717,18 +742,19 @@ def setCustomTitle(self): def save(self): try: - f = open('/tmp/epgimport.log', 'w') - f.write(log.getvalue()) + with open("/tmp/epgimport.log", "w") as f: + f.write(self.log.getvalue()) self.session.open(MessageBox, _("Write to /tmp/epgimport.log"), MessageBox.TYPE_INFO, timeout=5, close_on_any_key=True) - f.close() except Exception as e: - self["list"].setText("Failed to write /tmp/epgimport.log:str" + str(e)) + self["list"].setText(f"Failed to write /tmp/epgimport.log:str{str(e)}") self.close(True) def cancel(self): self.close(False) def clear(self): + self.log.logfile.seek(0) + self.log.logfile.truncate(0) self.close(False) @@ -753,16 +779,10 @@ def start_import(session, **kwargs): def main(session, **kwargs): session.openWithCallback(doneConfiguring, EPGImportConfig) -#def main_menu(menuid, **kwargs): -# if menuid == "mainmenu" and config.plugins.epgimport.showinmainmenu.getValue(): -# return [(_("EPG Importer"), start_import, "epgimporter", 45)] -# else: -# return [] - -def doneConfiguring(session, retval): - "user has closed configuration, check new values...." - if autoStartTimer is not None: +def doneConfiguring(session, retval=False): + """user has closed configuration, check new values....""" + if retval is True and autoStartTimer is not None: autoStartTimer.update() @@ -770,11 +790,13 @@ def doneImport(reboot=False, epgfile=None): global _session, lastImportResult, BouquetChannelListList, serviceIgnoreList BouquetChannelListList = None serviceIgnoreList = None - lastImportResult = (time.time(), epgimport.eventCount) + timestamp = time() + formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime(timestamp)) + lastImportResult = (formatted_time, epgimport.eventCount) try: start, count = lastImportResult - localtime = time.asctime(time.localtime(time.time())) - lastimport = "%s, %d" % (localtime, count) + current_time = asctime(localtime(time())) + lastimport = "%s, %d" % (current_time, count) config.plugins.extra_epgimport.last_import.value = lastimport config.plugins.extra_epgimport.last_import.save() print("[XMLTVImport] Save last import date and count event", file=log) @@ -789,7 +811,7 @@ def doneImport(reboot=False, epgfile=None): _session.openWithCallback(restartEnigma, MessageBox, msg, MessageBox.TYPE_YESNO, timeout=15, default=True) print("[XMLTVImport] Need restart enigma2", file=log) else: - if config.plugins.epgimport.parse_autotimer.value and fileExists("/usr/lib/enigma2/python/Plugins/Extensions/AutoTimer/plugin.py"): + if config.plugins.epgimport.parse_autotimer.value and isPluginInstalled("AutoTimer"): try: from Plugins.Extensions.AutoTimer.plugin import autotimer if autotimer is None: @@ -809,17 +831,18 @@ def doneImport(reboot=False, epgfile=None): class checkDeepstandby: def __init__(self, session, parse=False): self.session = session - if parse: - self.FirstwaitCheck = enigma.eTimer() - self.FirstwaitCheck.callback.append(self.runCheckDeepstandby) - self.FirstwaitCheck.startLongTimer(600) - print("[XMLTVImport] Wait for parse autotimers 30 sec.", file=log) - else: - self.runCheckDeepstandby() + if config.plugins.epgimport.enabled.value: + if parse: + self.FirstwaitCheck = eTimer() + self.FirstwaitCheck.callback.append(self.runCheckDeepstandby) + self.FirstwaitCheck.startLongTimer(600) + print("[XMLTVImport] Wait for parse autotimers 600 sec.", file=log) + else: + self.runCheckDeepstandby() def runCheckDeepstandby(self): print("[XMLTVImport] Run check deep standby after import", file=log) - if config.plugins.epgimport.shutdown.value and config.plugins.epgimport.deepstandby.value == 'wakeup': + if config.plugins.epgimport.shutdown.value and config.plugins.epgimport.deepstandby.value == "wakeup": if config.plugins.epgimport.deepstandby_afterimport.value and getFPWasTimerWakeup(): config.plugins.epgimport.deepstandby_afterimport.value = False if Screens.Standby.inStandby and not self.session.nav.getRecordings() and not Screens.Standby.inTryQuitMainloop: @@ -835,19 +858,18 @@ def restartEnigma(confirmed): # save state of enigma, so we can return to previeus state if Screens.Standby.inStandby: try: - open('/tmp/enigmastandby', 'wb').close() + open(STANDBY_FLAG_FILE, "wb").close() except: - print("Failed to create /tmp/enigmastandby", file=log) + print(f"Failed to create {STANDBY_FLAG_FILE}", file=log) else: try: - os.remove("/tmp/enigmastandby") + remove(STANDBY_FLAG_FILE) except: pass # now reboot _session.open(Screens.Standby.TryQuitMainloop, 3) -################################## # Autostart section class AutoStartTimer: @@ -855,44 +877,43 @@ def __init__(self, session): self.session = session self.prev_onlybouquet = config.plugins.epgimport.import_onlybouquet.value self.prev_multibouquet = config.usage.multibouquet.value - self.timer = enigma.eTimer() - self.timer.callback.append(self.onTimer) - self.pauseAfterFinishImportCheck = enigma.eTimer() + self.clock = config.plugins.epgimport.wakeup.value + self.autoStartImport = eTimer() + self.autoStartImport.callback.append(self.onTimer) + self.pauseAfterFinishImportCheck = eTimer() self.pauseAfterFinishImportCheck.callback.append(self.afterFinishImportCheck) self.pauseAfterFinishImportCheck.startLongTimer(30) self.update() def getWakeTime(self): if config.plugins.epgimport.enabled.value: - clock = config.plugins.epgimport.wakeup.value - nowt = time.time() - now = time.localtime(nowt) - return int(time.mktime((now.tm_year, now.tm_mon, now.tm_mday, clock[0], clock[1], lastMACbyte() // 5, 0, now.tm_yday, now.tm_isdst))) + nowt = time() + now = localtime(nowt) + return int(mktime((now.tm_year, now.tm_mon, now.tm_mday, self.clock[0], self.clock[1], lastMACbyte() // 5, 0, now.tm_yday, now.tm_isdst))) else: return -1 def update(self, atLeast=0): - self.timer.stop() + self.autoStartImport.stop() wake = self.getWakeTime() - now_t = time.time() + now_t = time() now = int(now_t) - now_day = time.localtime(now_t) + now_day = localtime(now_t) if wake > 0: cur_day = int(now_day.tm_wday) wakeup_day = WakeupDayOfWeek() if wakeup_day == -1: return -1 - print("[XMLTVImport] wakeup day of week disabled", file=log) if wake < now + atLeast: wake += 86400 * wakeup_day else: if not config.plugins.extra_epgimport.day_import[cur_day].value: wake += 86400 * wakeup_day next = wake - now - self.timer.startLongTimer(next) + self.autoStartImport.startLongTimer(next) else: wake = -1 - print("[XMLTVImport] WakeUpTime now set to", wake, "(now=%s)" % now, file=log) + print(f"[XMLTVImport] WakeUpTime now set to {wake} (now={now})", file=log) return wake def runImport(self): @@ -908,9 +929,9 @@ def runImport(self): startImport() def onTimer(self): - self.timer.stop() - now = int(time.time()) - print("[XMLTVImport] onTimer occured at", now, file=log) + self.autoStartImport.stop() + now = int(time()) + print(f"[XMLTVImport] onTimer occured at {now}", file=log) wake = self.getWakeTime() # If we're close enough, we're okay... atLeast = 0 @@ -928,15 +949,14 @@ def getSources(self): def getStatus(self): wake_up = self.getWakeTime() - now_t = time.time() + now_t = time() now = int(now_t) - now_day = time.localtime(now_t) + now_day = localtime(now_t) if wake_up > 0: cur_day = int(now_day.tm_wday) wakeup_day = WakeupDayOfWeek() if wakeup_day == -1: return -1 - print("[XMLTVImport] wakeup day of week disabled", file=log) if wake_up < now: wake_up += 86400 * wakeup_day else: @@ -947,12 +967,12 @@ def getStatus(self): return wake_up def afterFinishImportCheck(self): - if config.plugins.epgimport.deepstandby.value == 'wakeup' and getFPWasTimerWakeup(): - if os.path.exists("/tmp/enigmastandby") or os.path.exists("/tmp/.EPGImportAnswerBoot"): + if config.plugins.epgimport.deepstandby.value == "wakeup" and getFPWasTimerWakeup(): + if exists(STANDBY_FLAG_FILE) or exists("/tmp/.EPGImportAnswerBoot"): print("[XMLTVImport] is restart enigma2", file=log) else: wake = self.getStatus() - now_t = time.time() + now_t = time() now = int(now_t) if 0 < wake - now <= 60 * 5: if config.plugins.epgimport.standby_afterwakeup.value: @@ -966,7 +986,7 @@ def afterFinishImportCheck(self): print("[XMLTVImport] Run to standby after wake up for checking", file=log) if not config.plugins.epgimport.deepstandby_afterimport.value: config.plugins.epgimport.deepstandby_afterimport.value = True - self.wait_timer = enigma.eTimer() + self.wait_timer = eTimer() self.wait_timer.timeout.get().append(self.startStandby) print("[XMLTVImport] start wait_timer (10sec) for goto standby", file=log) self.wait_timer.start(10000, True) @@ -988,13 +1008,13 @@ def onLeaveStandby(self): def WakeupDayOfWeek(): start_day = -1 try: - now = time.time() - now_day = time.localtime(now) + now = time() + now_day = localtime(now) cur_day = int(now_day.tm_wday) except: cur_day = -1 if cur_day >= 0: - for i in (1, 2, 3, 4, 5, 6, 7): + for i in range(1, 8): if config.plugins.extra_epgimport.day_import[(cur_day + i) % 7].value: return i return start_day @@ -1003,32 +1023,33 @@ def WakeupDayOfWeek(): def onBootStartCheck(): global autoStartTimer print("[XMLTVImport] onBootStartCheck", file=log) - now = int(time.time()) + now = int(time()) wake = autoStartTimer.getStatus() - print("[XMLTVImport] now=%d wake=%d wake-now=%d" % (now, wake, wake - now), file=log) + print(f"[XMLTVImport] now={now} wake={wake} wake-now={wake - now}", file=log) if (wake < 0) or (wake - now > 600): + runboot = config.plugins.epgimport.runboot.value on_start = False - if config.plugins.epgimport.runboot.value == "1": + if runboot == "1": on_start = True print("[XMLTVImport] is boot", file=log) - elif config.plugins.epgimport.runboot.value == "2" and not getFPWasTimerWakeup(): + elif runboot == "2" and not getFPWasTimerWakeup(): on_start = True print("[XMLTVImport] is manual boot", file=log) - elif config.plugins.epgimport.runboot.value == "3" and getFPWasTimerWakeup(): + elif runboot == "3" and getFPWasTimerWakeup(): on_start = True print("[XMLTVImport] is automatic boot", file=log) - flag = '/tmp/.EPGImportAnswerBoot' - if config.plugins.epgimport.runboot_restart.value and config.plugins.epgimport.runboot.value != "3": - if os.path.exists(flag): + flag = "/tmp/.EPGImportAnswerBoot" + if config.plugins.epgimport.runboot_restart.value and runboot != "3": + if exists(flag): on_start = False print("[XMLTVImport] not starting import - is restart enigma2", file=log) else: try: - open(flag, 'wb').close() + open(flag, "wb").close() except: print("Failed to create /tmp/.EPGImportAnswerBoot", file=log) if config.plugins.epgimport.runboot_day.value: - now = time.localtime() + now = localtime() cur_day = int(now.tm_wday) if not config.plugins.extra_epgimport.day_import[cur_day].value: on_start = False @@ -1041,10 +1062,10 @@ def onBootStartCheck(): def autostart(reason, session=None, **kwargs): - "called with reason=1 to during shutdown, with reason=0 at startup?" + """called with reason=1 to during shutdown, with reason=0 at startup?""" global autoStartTimer global _session - print("[XMLTVImport] autostart (%s) occured at" % reason, time.time(), file=log) + print(f"[XMLTVImport] autostart ({reason}) occured at {int(time())}", file=log) if reason == 0 and _session is None: if session is not None: _session = session @@ -1053,12 +1074,12 @@ def autostart(reason, session=None, **kwargs): if config.plugins.epgimport.runboot.value != "4": onBootStartCheck() # If WE caused the reboot, put the box back in standby. - if os.path.exists("/tmp/enigmastandby"): + if exists(STANDBY_FLAG_FILE): print("[XMLTVImport] Returning to standby", file=log) if not Screens.Standby.inStandby: Notifications.AddNotification(Screens.Standby.Standby) try: - os.remove("/tmp/enigmastandby") + remove(STANDBY_FLAG_FILE) except: pass else: @@ -1066,16 +1087,15 @@ def autostart(reason, session=None, **kwargs): def getNextWakeup(): - "returns timestamp of next time when autostart should be called" + """returns timestamp of next time when autostart should be called""" if autoStartTimer: - if config.plugins.epgimport.deepstandby.value == 'wakeup' and autoStartTimer.getSources(): + if config.plugins.epgimport.enabled.value and config.plugins.epgimport.deepstandby.value == "wakeup" and autoStartTimer.getSources(): print("[XMLTVImport] Will wake up from deep sleep", file=log) return autoStartTimer.getStatus() return -1 -# we need this helper function to identify the descriptor - +# we need this helper function to identify the descriptor def extensionsmenu(session, **kwargs): main(session, **kwargs) @@ -1087,13 +1107,13 @@ def setExtensionsmenu(el): else: Components.PluginComponent.plugins.removePlugin(extDescriptor) except Exception as e: - print("[EPGImport] Failed to update extensions menu:", e) + print(f"[EPGImport] Failed to update extensions menu:{e}") description = _("Automated EPG Importer") config.plugins.epgimport.showinextensions.addNotifier(setExtensionsmenu, initial_call=False, immediate_feedback=False) -extDescriptor = PluginDescriptor(name=_("EPG-Importer"), description=description, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=extensionsmenu) -pluginlist = PluginDescriptor(name=_("EPG-Importer"), description=description, where=PluginDescriptor.WHERE_PLUGINMENU, icon='plugin.png', fnc=main) +extDescriptor = PluginDescriptor(name=_("EPG-Importer Now"), description=description, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=start_import) +pluginlist = PluginDescriptor(name=_("EPG-Importer"), description=description, where=PluginDescriptor.WHERE_PLUGINMENU, icon="plugin.png", fnc=main) def epgmenu(menuid, **kwargs): @@ -1143,26 +1163,3 @@ def Plugins(**kwargs): if config.plugins.epgimport.showinplugins.value: result.append(pluginlist) return result - - -class SetupSummary(Screen): - def __init__(self, session, parent): - Screen.__init__(self, session, parent=parent) - self["SetupTitle"] = StaticText(_(parent.setup_title)) - self["SetupEntry"] = StaticText("") - self["SetupValue"] = StaticText("") - self.onShow.append(self.addWatcher) - self.onHide.append(self.removeWatcher) - - def addWatcher(self): - self.parent.onChangedEntry.append(self.selectionChanged) - self.parent["list"].onSelectionChanged.append(self.selectionChanged) - self.selectionChanged() - - def removeWatcher(self): - self.parent.onChangedEntry.remove(self.selectionChanged) - self.parent["list"].onSelectionChanged.remove(self.selectionChanged) - - def selectionChanged(self): - self["SetupEntry"].text = self.parent.getCurrentEntry() - self["SetupValue"].text = self.parent.getCurrentValue() diff --git a/src/EPGImport/xmltvconverter.py b/src/EPGImport/xmltvconverter.py index 25fae4f..cc5f2e3 100755 --- a/src/EPGImport/xmltvconverter.py +++ b/src/EPGImport/xmltvconverter.py @@ -1,55 +1,63 @@ -from __future__ import absolute_import -from __future__ import print_function -import time -import calendar -import six +from xml.etree.cElementTree import iterparse +from calendar import timegm +from time import strptime, struct_time from . import log -#from pprint import pprint -from xml.etree.cElementTree import ElementTree, Element, SubElement, tostring, iterparse -# %Y%m%d%H%M%S - -def quickptime(str): - return time.struct_time((int(str[0:4]), int(str[4:6]), int(str[6:8]), - int(str[8:10]), int(str[10:12]), 0, - -1, -1, 0)) +def quickptime(date_str): + return struct_time( + ( + int(date_str[0:4]), # Year + int(date_str[4:6]), # Month + int(date_str[6:8]), # Day + int(date_str[8:10]), # Hour + int(date_str[10:12]), # Minute + 0, # Second (set to 0) + -1, # Weekday (set to -1 as unknown) + -1, # Julian day (set to -1 as unknown) + 0 # DST (Daylight Saving Time, set to 0 as unknown) + ) + ) def get_time_utc(timestring, fdateparse): - #print "get_time_utc", timestring, format + # print("get_time_utc", timestring, format) try: - values = timestring.split(' ') + values = timestring.split(" ") tm = fdateparse(values[0]) - timegm = calendar.timegm(tm) - #suppose file says +0300 => that means we have to substract 3 hours from localtime to get gmt - timegm -= (3600 * int(values[1]) // 100) - return timegm + time_gm = timegm(tm) + # suppose file says +0300 => that means we have to substract 3 hours from localtime to get GMT + time_gm -= (3600 * int(values[1]) // 100) + return time_gm except Exception as e: - print("[XMLTVConverter] get_time_utc error:", e) + print(f"[XMLTVConverter] get_time_utc error:{e}") return 0 + # Preferred language should be configurable, but for now, # we just like Dutch better! - - def get_xml_string(elem, name): - r = '' + r = "" try: for node in elem.findall(name): txt = node.text - lang = node.get('lang', None) - if not r: + lang = node.get("lang", None) + if not r and txt is not None: r = txt elif lang == "nl": r = txt except Exception as e: - print("[XMLTVConverter] get_xml_string error:", e) + print(f"[XMLTVConverter] get_xml_string error: {e}") + """ # Now returning UTF-8 by default, the epgdat/oudeis must be adjusted to make this work. - return six.ensure_str(r) + # Note that the default xml.sax.saxutils.unescape() function don't unescape + # some characters and we have to manually add them to the entities dictionary. + """ + return r.decode() if isinstance(r, bytes) else r + def get_xml_rating_string(elem): - r = '' + r = "" try: for node in elem.findall("rating"): for val in node.findall("value"): @@ -57,30 +65,34 @@ def get_xml_rating_string(elem): if not r: r = txt except Exception as e: - print("[XMLTVConverter] get_xml_rating_string error:", e) - return six.ensure_str(r) + print(f"[XMLTVConverter] get_xml_rating_string error:{e}") + return r.decode() if isinstance(r, bytes) else r + def enumerateProgrammes(fp): """Enumerates programme ElementTree nodes from file object 'fp'""" for event, elem in iterparse(fp): - if elem.tag == 'programme': - yield elem - elem.clear() - elif elem.tag == 'channel': - # Throw away channel elements, save memory - elem.clear() + try: + if elem.tag == "programme": + yield elem + elem.clear() + elif elem.tag == "channel": + # Throw away channel elements, save memory + elem.clear() + except Exception as e: + print(f"[XMLTVConverter] enumerateProgrammes error:{e}") class XMLTVConverter: - def __init__(self, channels_dict, category_dict, dateformat='%Y%m%d%H%M%S %Z', offset=0): + def __init__(self, channels_dict, category_dict, dateformat="%Y%m%d%H%M%S %Z", offset=0): self.channels = channels_dict self.categories = category_dict - if dateformat.startswith('%Y%m%d%H%M%S'): + if dateformat.startswith("%Y%m%d%H%M%S"): self.dateParser = quickptime else: - self.dateParser = lambda x: time.strptime(x, dateformat) + self.dateParser = lambda x: strptime(x, dateformat) self.offset = offset - print("[XMLTVConverter] Using a custom time offset of %d" % offset) + print(f"[XMLTVConverter] Using a custom time offset of {offset}") def enumFile(self, fileobj): print("[XMLTVConverter] Enumerating event information", file=log) @@ -89,56 +101,56 @@ def enumFile(self, fileobj): if not self.channels: return for elem in enumerateProgrammes(fileobj): - channel = elem.get('channel') + channel = elem.get("channel") channel = channel.lower() - if not channel in self.channels: + if channel not in self.channels: if lastUnknown != channel: - print("Unknown channel: ", channel, file=log) + print(f"Unknown channel: {channel}", file=log) lastUnknown = channel # return a None object to give up time to the reactor. yield None continue try: services = self.channels[channel] - start = get_time_utc(elem.get('start'), self.dateParser) + self.offset - stop = get_time_utc(elem.get('stop'), self.dateParser) + self.offset - title = get_xml_string(elem, 'title') + start = get_time_utc(elem.get("start"), self.dateParser) + self.offset + stop = get_time_utc(elem.get("stop"), self.dateParser) + self.offset + title = get_xml_string(elem, "title") # try/except for EPG XML files with program entries containing try: - subtitle = get_xml_string(elem, 'sub-title') + subtitle = get_xml_string(elem, "sub-title") except: - subtitle = '' + subtitle = "" # try/except for EPG XML files with program entries containing try: - description = get_xml_string(elem, 'desc') + description = get_xml_string(elem, "desc") except: - description = '' - category = get_xml_string(elem, 'category') + description = "" + category = get_xml_string(elem, "category") cat_nr = self.get_category(category, stop - start) try: rating_str = get_xml_rating_string(elem) # hardcode country as ENG since there is no handling for parental certification systems per country yet # also we support currently only number like values like "12+" since the epgcache works only with bytes right now - rating = [("eng", int(rating_str)-3)] + rating = [("eng", int(rating_str) - 3)] except: rating = None # data_tuple = (data.start, data.duration, data.title, data.short_description, data.long_description, data.type) if not stop or not start or (stop <= start): - print("[XMLTVConverter] Bad start/stop time: %s (%s) - %s (%s) [%s]" % (elem.get('start'), start, elem.get('stop'), stop, title)) + print(f"[XMLTVConverter] Bad start/stop time: {elem.get('start')} ({start}) - {elem.get('stop')} ({stop}) [{title}]") if rating: yield (services, (start, stop - start, title, subtitle, description, cat_nr, 0, rating)) else: yield (services, (start, stop - start, title, subtitle, description, cat_nr)) except Exception as e: - print("[XMLTVConverter] parsing event error:", e) + print(f"[XMLTVConverter] parsing event error: {e}") - def get_category(self, str, duration): - if (not str) or (not isinstance(str, type('str'))): + def get_category(self, cat, duration): + if (not cat) or (not isinstance(cat, type("str"))): return 0 - if str in self.categories: - category = self.categories[str] + if cat in self.categories: + category = self.categories[cat] if len(category) > 1: if duration > 60 * category[1]: return category[0]