From 87474c56dd24e4fdabc3df6b2ed9a80e6a4e11af Mon Sep 17 00:00:00 2001 From: Alhaziel01 Date: Tue, 7 Jul 2020 17:35:29 +0200 Subject: [PATCH] Test Installazione Widevine --- core/downloadtools.py | 6 +- lib/arm_chromeos.py | 333 ++++++++++++++++++ platformcode/platformtools.py | 183 +++++++++- .../resource.language.en_gb/strings.po | 36 ++ .../resource.language.it_it/strings.po | 36 ++ 5 files changed, 590 insertions(+), 4 deletions(-) create mode 100644 lib/arm_chromeos.py diff --git a/core/downloadtools.py b/core/downloadtools.py index 1747b435..b0a35cb0 100644 --- a/core/downloadtools.py +++ b/core/downloadtools.py @@ -216,12 +216,14 @@ def downloadbest(video_urls, title, continuar=False): return -2 -def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False, resumir=True): +def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False, resumir=True, header=''): logger.info("url= " + url) logger.info("filename= " + nombrefichero) if headers is None: headers = [] + if not header: + header = "plugin" progreso = None @@ -269,7 +271,7 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False # Create the progress dialog if not silent: - progreso = platformtools.dialog_progress("plugin", "Downloading...", url, nombrefichero) + progreso = platformtools.dialog_progress(header, "Downloading...", url, nombrefichero) # If the platform does not return a valid dialog box, it assumes silent mode if progreso is None: diff --git a/lib/arm_chromeos.py b/lib/arm_chromeos.py new file mode 100644 index 00000000..d4808402 --- /dev/null +++ b/lib/arm_chromeos.py @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +# MIT License (see LICENSE.txt or https://opensource.org/licenses/MIT) +# From inputstreamhelper +"""Implements a class with methods related to the Chrome OS image""" + +from __future__ import absolute_import, division, unicode_literals +import os +from struct import calcsize, unpack +from zipfile import ZipFile +from io import UnsupportedOperation +from platformcode import logger, config + +def compat_path(path, encoding='utf-8', errors='strict'): + """Convert unicode path to bytestring if needed""" + import sys + if (sys.version_info.major == 2 and isinstance(path, unicode) # noqa: F821; pylint: disable=undefined-variable,useless-suppression + and not sys.platform.startswith('win')): + return path.encode(encoding, errors) + return path + + +class ChromeOSImage: + """ + The main class handling a Chrome OS image + + Information related to ext2 is sourced from here: https://www.nongnu.org/ext2-doc/ext2.html + """ + + def __init__(self, imgpath): + logger.info('Image Path: ' + imgpath) + """Prepares the image""" + self.imgpath = imgpath + self.bstream = self.get_bstream(imgpath) + self.part_offset = None + self.sb_dict = None + self.blocksize = None + self.blk_groups = None + self.progress = None + + def gpt_header(self): + """Returns the needed parts of the GPT header, can be easily expanded if necessary""" + header_fmt = '<8s4sII4x4Q16sQ3I' + header_size = calcsize(header_fmt) + lba_size = 512 # assuming LBA size + self.seek_stream(lba_size) + + # GPT Header entries: signature, revision, header_size, header_crc32, (reserved 4x skipped,) current_lba, backup_lba, + # first_usable_lba, last_usable_lba, disk_guid, start_lba_part_entries, num_part_entries, + # size_part_entry, crc32_part_entries + _, _, _, _, _, _, _, _, _, start_lba_part_entries, num_part_entries, size_part_entry, _ = unpack(header_fmt, self.read_stream(header_size)) + + return (start_lba_part_entries, num_part_entries, size_part_entry) + + def chromeos_offset(self): + """Calculate the Chrome OS losetup start offset""" + part_format = '<16s16sQQQ72s' + entries_start, entries_num, entry_size = self.gpt_header() # assuming partition table is GPT + lba_size = 512 # assuming LBA size + self.seek_stream(entries_start * lba_size) + + if not calcsize(part_format) == entry_size: + logger.info('Partition table entries are not 128 bytes long') + return 0 + + for index in range(1, entries_num + 1): # pylint: disable=unused-variable + # Entry: type_guid, unique_guid, first_lba, last_lba, attr_flags, part_name + _, _, first_lba, _, _, part_name = unpack(part_format, self.read_stream(entry_size)) + part_name = part_name.decode('utf-16').strip('\x00') + if part_name == 'ROOT-A': # assuming partition name is ROOT-A + offset = first_lba * lba_size + break + + if not offset: + logger.info('Failed to calculate losetup offset.') + return 0 + + return offset + + def extract_file(self, filename, extract_path, progress): + """Extracts the file from the image""" + self.progress = progress + + self.progress.update(2, config.get_localized_string(70813)) + self.part_offset = self.chromeos_offset() + self.sb_dict = self.superblock() + self.blk_groups = self.block_groups() + + bin_filename = filename.encode('ascii') + chunksize = 4 * 1024**2 + percent8 = 40 + self.progress.update(int(percent8 / 8), config.get_localized_string(70814)) + chunk1 = self.read_stream(chunksize) + while True: + chunk2 = self.read_stream(chunksize) + if not chunk2: + logger.info('File %s not found in the ChromeOS image' % filename) + return False + + chunk = chunk1 + chunk2 + if bin_filename in chunk: + i_index_pos = chunk.index(bin_filename) - 8 + dir_dict = self.dir_entry(chunk[i_index_pos:i_index_pos + len(filename) + 8]) + if dir_dict['inode'] < self.sb_dict['s_inodes_count'] and dir_dict['name_len'] == len(filename): + break + chunk1 = chunk2 + if percent8 < 240: + percent8 += 1 + self.progress.update(int(percent8 / 8)) + + self.progress.update(32, config.get_localized_string(70815)) + + blk_group_num = (dir_dict['inode'] - 1) // self.sb_dict['s_inodes_per_group'] + blk_group = self.blk_groups[blk_group_num] + i_index_in_group = (dir_dict['inode'] - 1) % self.sb_dict['s_inodes_per_group'] + + inode_pos = self.part_offset + self.blocksize * blk_group['bg_inode_table'] + self.sb_dict['s_inode_size'] * i_index_in_group + inode_dict, _ = self.inode_table(inode_pos) + + return self.write_file(inode_dict, os.path.join(extract_path, filename)) + + def superblock(self): + """Get relevant info from the superblock, assert it's an ext2 fs""" + names = ('s_inodes_count', 's_blocks_count', 's_r_blocks_count', 's_free_blocks_count', 's_free_inodes_count', 's_first_data_block', + 's_log_block_size', 's_log_frag_size', 's_blocks_per_group', 's_frags_per_group', 's_inodes_per_group', 's_mtime', 's_wtime', + 's_mnt_count', 's_max_mnt_count', 's_magic', 's_state', 's_errors', 's_minor_rev_level', 's_lastcheck', 's_checkinterval', + 's_creator_os', 's_rev_level', 's_def_resuid', 's_def_resgid', 's_first_ino', 's_inode_size', 's_block_group_nr', + 's_feature_compat', 's_feature_incompat', 's_feature_ro_compat', 's_uuid', 's_volume_name', 's_last_mounted', + 's_algorithm_usage_bitmap', 's_prealloc_block', 's_prealloc_dir_blocks') + fmt = '<13I6H4I2HI2H3I16s16s64sI2B818x' + fmt_len = calcsize(fmt) + + self.seek_stream(self.part_offset + 1024) # superblock starts after 1024 byte + pack = self.read_stream(fmt_len) + sb_dict = dict(zip(names, unpack(fmt, pack))) + + sb_dict['s_magic'] = hex(sb_dict['s_magic']) + assert sb_dict['s_magic'] == '0xef53' # assuming/checking this is an ext2 fs + + block_groups_count1 = sb_dict['s_blocks_count'] / sb_dict['s_blocks_per_group'] + block_groups_count1 = int(block_groups_count1) if float(int(block_groups_count1)) == block_groups_count1 else int(block_groups_count1) + 1 + block_groups_count2 = sb_dict['s_inodes_count'] / sb_dict['s_inodes_per_group'] + block_groups_count2 = int(block_groups_count2) if float(int(block_groups_count2)) == block_groups_count2 else int(block_groups_count2) + 1 + assert block_groups_count1 == block_groups_count2 + sb_dict['block_groups_count'] = block_groups_count1 + + self.blocksize = 1024 << sb_dict['s_log_block_size'] + + return sb_dict + + def block_group(self): + """Get info about a block group""" + names = ('bg_block_bitmap', 'bg_inode_bitmap', 'bg_inode_table', 'bg_free_blocks_count', 'bg_free_inodes_count', 'bg_used_dirs_count', 'bg_pad') + fmt = '<3I4H12x' + fmt_len = calcsize(fmt) + + pack = self.read_stream(fmt_len) + blk = unpack(fmt, pack) + + blk_dict = dict(zip(names, blk)) + + return blk_dict + + def block_groups(self): + """Get info about all block groups""" + if self.blocksize == 1024: + self.seek_stream(self.part_offset + 2 * self.blocksize) + else: + self.seek_stream(self.part_offset + self.blocksize) + + blk_groups = [] + for i in range(self.sb_dict['block_groups_count']): # pylint: disable=unused-variable + blk_group = self.block_group() + blk_groups.append(blk_group) + + return blk_groups + + def inode_table(self, inode_pos): + """Reads and returns an inode table and inode size""" + names = ('i_mode', 'i_uid', 'i_size', 'i_atime', 'i_ctime', 'i_mtime', 'i_dtime', 'i_gid', 'i_links_count', 'i_blocks', 'i_flags', + 'i_osd1', 'i_block0', 'i_block1', 'i_block2', 'i_block3', 'i_block4', 'i_block5', 'i_block6', 'i_block7', 'i_block8', + 'i_block9', 'i_block10', 'i_block11', 'i_blocki', 'i_blockii', 'i_blockiii', 'i_generation', 'i_file_acl', 'i_dir_acl', 'i_faddr') + fmt = '<2Hi4I2H3I15I4I12x' + fmt_len = calcsize(fmt) + inode_size = self.sb_dict['s_inode_size'] + + self.seek_stream(inode_pos) + pack = self.read_stream(fmt_len) + inode = unpack(fmt, pack) + + inode_dict = dict(zip(names, inode)) + inode_dict['i_mode'] = hex(inode_dict['i_mode']) + + blocks = inode_dict['i_size'] / self.blocksize + inode_dict['blocks'] = int(blocks) if float(int(blocks)) == blocks else int(blocks) + 1 + + self.read_stream(inode_size - fmt_len) + return inode_dict, inode_size + + @staticmethod + def dir_entry(chunk): + """Returns the directory entry found in chunk""" + dir_names = ('inode', 'rec_len', 'name_len', 'file_type', 'name') + dir_fmt = '= self.bstream[1]: + while seek_pos - self.bstream[1] > chunksize: + self.read_stream(chunksize) + self.read_stream(seek_pos - self.bstream[1]) + return + + self.bstream[0].close() + self.bstream[1] = 0 + self.bstream = self.get_bstream(self.imgpath) + + while seek_pos - self.bstream[1] > chunksize: + self.read_stream(chunksize) + self.read_stream(seek_pos - self.bstream[1]) + + return + + def read_stream(self, num_of_bytes): + """Read and return a chunk of the bytestream""" + self.bstream[1] += num_of_bytes + + return self.bstream[0].read(num_of_bytes) + + def get_block_ids(self, inode_dict): + """Get all block indices/IDs of an inode""" + ids_to_read = inode_dict['blocks'] + block_ids = [inode_dict['i_block' + str(i)] for i in range(12)] + ids_to_read -= 12 + + if not inode_dict['i_blocki'] == 0: + iblocks, ids_to_read = self.iblock_ids(inode_dict['i_blocki'], ids_to_read) + block_ids += iblocks + if not inode_dict['i_blockii'] == 0: + iiblocks, ids_to_read = self.iiblock_ids(inode_dict['i_blockii'], ids_to_read) + block_ids += iiblocks + + return block_ids[:inode_dict['blocks']] + + def read_file(self, block_ids): + """Read blocks specified by IDs into a dict""" + block_dict = {} + for block_id in block_ids: + percent = int(35 + 60 * block_ids.index(block_id) / len(block_ids)) + self.progress.update(percent, config.get_localized_string(70816)) + seek_pos = self.part_offset + self.blocksize * block_id + self.seek_stream(seek_pos) + block_dict[block_id] = self.read_stream(self.blocksize) + + return block_dict + + @staticmethod + def write_file_chunk(opened_file, chunk, bytes_to_write): + """Writes bytes to file in chunks""" + if len(chunk) > bytes_to_write: + opened_file.write(chunk[:bytes_to_write]) + return 0 + + opened_file.write(chunk) + return bytes_to_write - len(chunk) + + def write_file(self, inode_dict, filepath): + """Writes file specified by its inode to filepath""" + bytes_to_write = inode_dict['i_size'] + block_ids = self.get_block_ids(inode_dict) + + block_ids_sorted = block_ids[:] + block_ids_sorted.sort() + block_dict = self.read_file(block_ids_sorted) + + write_dir = os.path.join(os.path.dirname(filepath), '') + if not os.path.exists(write_dir): + os.mkdir(write_dir) + + with open(compat_path(filepath), 'wb') as opened_file: + for block_id in block_ids: + bytes_to_write = self.write_file_chunk(opened_file, block_dict[block_id], bytes_to_write) + if bytes_to_write == 0: + return True + + return False + + @staticmethod + def get_bstream(imgpath): + """Get a bytestream of the image""" + if imgpath.endswith('.zip'): + bstream = ZipFile(imgpath, 'r').open(os.path.basename(imgpath).strip('.zip'), 'r') + else: + bstream = open(imgpath, 'rb') + + return [bstream, 0] diff --git a/platformcode/platformtools.py b/platformcode/platformtools.py index 7b1d9e32..9ff0713f 100644 --- a/platformcode/platformtools.py +++ b/platformcode/platformtools.py @@ -248,7 +248,7 @@ def getCurrentView(item=None, parent_item=None): elif (item.contentType in ['movie'] and parent_item.action in parent_actions) \ or (item.channel in ['videolibrary'] and parent_item.action in ['list_movies']) \ or (parent_item.channel in ['favorites'] and parent_item.action in ['mainlist']) \ - or parent_item.action in ['now_on_tv', 'now_on_misc', 'now_on_misc_film', 'mostrar_perfil', 'live']: + or parent_item.action in ['now_on_tv', 'now_on_misc', 'now_on_misc_film', 'mostrar_perfil', 'live', 'replay']: return 'movie', 'movies' elif (item.contentType in ['tvshow'] and parent_item.action in parent_actions) \ @@ -505,7 +505,7 @@ def is_playing(): def play_video(item, strm=False, force_direct=False, autoplay=False): logger.info() - # logger.debug(item.tostring('\n')) + logger.debug(item.tostring('\n')) if item.channel == 'downloads': logger.info("Play local video: %s [%s]" % (item.title, item.url)) xlistitem = xbmcgui.ListItem(path=item.url) @@ -548,6 +548,11 @@ def play_video(item, strm=False, force_direct=False, autoplay=False): install_inputstream() xlistitem.setProperty('inputstreamaddon', 'inputstream.adaptive') xlistitem.setProperty('inputstream.adaptive.manifest_type', 'mpd') + if item.drm and item.license: + install_widevine() + xlistitem.setProperty("inputstream.adaptive.license_type", item.drm) + xlistitem.setProperty("inputstream.adaptive.license_key", item.license) + xlistitem.setMimeType('application/dash+xml') if force_direct: item.play_from = 'window' @@ -1067,6 +1072,9 @@ def resume_playback(item, return_played_time=False): item.nfo = item.strm_path = "" return item, None, None, None + +##### INPUTSTREM ##### + def install_inputstream(): from xbmcaddon import Addon if not os.path.exists(os.path.join(xbmc.translatePath('special://home/addons/'),'inputstream.adaptive')) and not os.path.exists(os.path.join(xbmc.translatePath('special://xbmcbinaddons/'),'inputstream.adaptive')): @@ -1086,3 +1094,174 @@ def install_inputstream(): except: xbmc.executebuiltin('UpdateLocalAddons') xbmc.executeJSONRPC('{"jsonrpc": "2.0", "id":1, "method": "Addons.SetAddonEnabled", "params": { "addonid": "inputstream.adaptive", "enabled": true }}') + + +def install_widevine(): + platform = get_platform() + if platform['os'] != 'android': + from core.httptools import downloadpage + from xbmcaddon import Addon + from core import jsontools + from distutils.version import LooseVersion + path = xbmc.translatePath(Addon('inputstream.adaptive').getSetting('DECRYPTERPATH')) + + # if Widevine CDM is not installed + if not os.path.exists(path): + select = dialog_yesno('Widevine CDM', config.get_localized_string(70808)) + if select > 0: + if not 'arm' in platform['arch']: + last_version = downloadpage('https://dl.google.com/widevine-cdm/versions.txt').data.split()[-1] + download_widevine(last_version, platform, path) + else: + json = downloadpage('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json').data + devices = jsontools.load(json) + download_chromeos_image(devices, platform, path) + + # if Widevine CDM is outdated + elif platform['os'] != 'android': + select = dialog_yesno(config.get_localized_string(70810),config.get_localized_string(70809)) + if select > 0: + if not 'arm' in platform['arch']: + last_version = downloadpage('https://dl.google.com/widevine-cdm/versions.txt').data.split()[-1] + current_version = jsontools.load(open(os.path.join(path, 'manifest.json')).read())['version'] + if LooseVersion(last_version) > LooseVersion(current_version): + download_widevine(last_version, platform, path) + else: + devices = jsontools.load(downloadpage('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json').data) + current_version = jsontools.load(open(os.path.join(path, 'config.json')).read())['version'] + last_version = best_chromeos_image(devices)['version'] + if LooseVersion(last_version) > LooseVersion(current_version): + download_chromeos_image(devices, platform, path) + + +def download_widevine(version, platform, path): + # for x86 architectures + from zipfile import ZipFile + from xbmcaddon import Addon + from core import downloadtools + archiveName = 'https://dl.google.com/widevine-cdm/' + version + '-' + platform['os'] + '-' + platform['arch'] + '.zip' + fileName = config.get_temp_file('widevine.zip') + if not os.path.exists(archiveName): + if not os.path.exists(fileName): + downloadtools.downloadfile(archiveName, fileName, header='Download Widevine CDM') + zip_obj = ZipFile(fileName) + for filename in zip_obj.namelist(): + zip_obj.extract(filename, path) + zip_obj.close() + os.remove(fileName) + + +def download_chromeos_image(devices, platform, path): + # for arm architectures + from core import downloadtools + from zipfile import ZipFile + from core import jsontools + best = best_chromeos_image(devices) + archiveName = best['url'] + version = best['version'] + + fileName = config.get_temp_file(archiveName.split('/')[-1]) + if not os.path.exists(fileName): + downloadtools.downloadfile(archiveName, fileName, header='Download Widevine CDM') + from lib.arm_chromeos import ChromeOSImage + ChromeOSImage(fileName).extract_file( + filename='libwidevinecdm.so', + extract_path=os.path.join(path), + progress=dialog_progress(config.get_localized_string(70811),config.get_localized_string(70812))) + recovery_file = os.path.join(path, os.path.basename('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json')) + config_file = os.path.join(path, 'config.json') + if not os.path.exists(path): + os.mkdir(path) + with open(recovery_file, 'w') as reco_file: + reco_file.write(jsontools.dump(devices, indent=4)) + reco_file.close() + with open(config_file, 'w') as conf_file: + conf_file.write(jsontools.dump(best)) + conf_file.close() + os.remove(fileName) + +def best_chromeos_image(devices): + best = None + for device in devices: + # Select ARM hardware only + for arm_hwid in ['BIG','BLAZE','BOB','DRUWL','DUMO','ELM','EXPRESSO','FIEVEL','HANA','JAQ','JERRY','KEVIN','KITTY','MICKEY','MIGHTY','MINNIE','PHASER','PHASER360','PI','PIT','RELM','SCARLET','SKATE','SNOW','SPEEDY','SPRING','TIGER']: + if arm_hwid in device['hwidmatch']: + hwid = arm_hwid + break # We found an ARM device, rejoice ! + else: + continue # Not ARM, skip this device + + device['hwid'] = hwid + + # Select the first ARM device + if best is None: + best = device + continue # Go to the next device + + # Skip identical hwid + if hwid == best['hwid']: + continue + + # Select the newest version + from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module,useless-suppression + if LooseVersion(device['version']) > LooseVersion(best['version']): + logger.info('%s (%s) is newer than %s (%s)' % (device['hwid'], device['version'], best['hwid'], best['version'])) + best = device + + # Select the smallest image (disk space requirement) + elif LooseVersion(device['version']) == LooseVersion(best['version']): + if int(device['filesize']) + int(device['zipfilesize']) < int(best['filesize']) + int(best['zipfilesize']): + logger.info('%s (%d) is smaller than %s (%d)' % (device['hwid'], int(device['filesize']) + int(device['zipfilesize']), best['hwid'], int(best['filesize']) + int(best['zipfilesize']))) + best = device + return best + +def get_platform(): + import platform + build = xbmc.getInfoLabel("System.BuildVersion") + kodi_version = int(build.split()[0][:2]) + ret = { + "auto_arch": sys.maxsize > 2 ** 32 and "64-bit" or "32-bit", + "arch": sys.maxsize > 2 ** 32 and "x64" or "ia32", + "os": "", + "version": platform.release(), + "kodi": kodi_version, + "build": build + } + if xbmc.getCondVisibility("system.platform.android"): + ret["os"] = "android" + if "arm" in platform.machine() or "aarch" in platform.machine(): + ret["arch"] = "arm" + if "64" in platform.machine() and ret["auto_arch"] == "64-bit": + ret["arch"] = "arm64" + elif xbmc.getCondVisibility("system.platform.linux"): + ret["os"] = "linux" + if "aarch" in platform.machine() or "arm64" in platform.machine(): + if xbmc.getCondVisibility("system.platform.linux.raspberrypi"): + ret["arch"] = "armv7" + elif ret["auto_arch"] == "32-bit": + ret["arch"] = "armv7" + elif ret["auto_arch"] == "64-bit": + ret["arch"] = "arm64" + elif platform.architecture()[0].startswith("32"): + ret["arch"] = "arm" + else: + ret["arch"] = "arm64" + elif "armv7" in platform.machine(): + ret["arch"] = "armv7" + elif "arm" in platform.machine(): + ret["arch"] = "arm" + elif xbmc.getCondVisibility("system.platform.xbox"): + ret["os"] = "win" + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.windows"): + ret["os"] = "win" + if platform.machine().endswith('64'): + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.osx"): + ret["os"] = "mac" + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.ios"): + ret["os"] = "ios" + ret["arch"] = "arm" + + return ret \ No newline at end of file diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index c4f3db0d..e90eead2 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -6031,6 +6031,42 @@ msgctxt "#70807" msgid "Elementum does not support network folder downloads, do you want to change the download location?" msgstr "Elementum non supporta i download su cartella di rete, vuoi cambiare il percorso di download?" +msgctxt "#70808" +msgid "In order to view this content you need to install [B]Widevine CDM[/B]. Do you want to install it?" +msgstr "Per poter visionare questo contenuto devi installare [B]Widevine CDM[/B]. Vuoi installarlo?" + +msgctxt "#70809" +msgid "An update of [B]Widevine CDM[/B] is available. Do you want to install it?" +msgstr "È disponibile un aggiornamento di [B]Widevine CDM[/B]. Vuoi installarlo?" + +msgctxt "#70810" +msgid "Update available" +msgstr "Aggiornamento disponibile" + +msgctxt "#70811" +msgid "Extracting Widevine CDM" +msgstr "Estrazione di Widevine CDM" + +msgctxt "#70812" +msgid "Preparing downloaded image..." +msgstr "Preparazione dell'immagine scaricata..." + +msgctxt "#70813" +msgid "Identifying wanted partition..." +msgstr "Identificazione della partizione desiderata..." + +msgctxt "#70814" +msgid "Scanning the filesystem for the Widevine CDM..." +msgstr "Scansione del filesystem per Widevine CDM..." + +msgctxt "#70815" +msgid "Widevine CDM found, analyzing..." +msgstr "Widevine CDM trovato, analisi..." + +msgctxt "#70816" +msgid "Extracting Widevine CDM from image..." +msgstr "Estrazione di Widevine CDM dall'immagine..." + # DNS start [ settings and declaration ] msgctxt "#707401" msgid "Enable DNS check alert" diff --git a/resources/language/resource.language.it_it/strings.po b/resources/language/resource.language.it_it/strings.po index d444157b..027c8c69 100644 --- a/resources/language/resource.language.it_it/strings.po +++ b/resources/language/resource.language.it_it/strings.po @@ -6031,6 +6031,42 @@ msgctxt "#70807" msgid "Elementum does not support network folder downloads, do you want to change the download location?" msgstr "Elementum non supporta i download su cartella di rete, vuoi cambiare il percorso di download?" +msgctxt "#70808" +msgid "In order to view this content you need to install [B]Widevine CDM[/B]. Do you want to install it?" +msgstr "Per poter visionare questo contenuto devi installare [B]Widevine CDM[/B]. Vuoi installarlo?" + +msgctxt "#70809" +msgid "An update of [B]Widevine CDM[/B] is available. Do you want to install it?" +msgstr "È disponibile un aggiornamento di [B]Widevine CDM[/B]. Vuoi installarlo?" + +msgctxt "#70810" +msgid "Update available" +msgstr "Aggiornamento disponibile" + +msgctxt "#70811" +msgid "Extracting Widevine CDM" +msgstr "Estrazione di Widevine CDM" + +msgctxt "#70812" +msgid "Preparing downloaded image..." +msgstr "Preparazione dell'immagine scaricata..." + +msgctxt "#70813" +msgid "Identifying wanted partition..." +msgstr "Identificazione della partizione desiderata..." + +msgctxt "#70814" +msgid "Scanning the filesystem for the Widevine CDM..." +msgstr "Scansione del filesystem per Widevine CDM..." + +msgctxt "#70815" +msgid "Widevine CDM found, analyzing..." +msgstr "Widevine CDM trovato, analisi..." + +msgctxt "#70816" +msgid "Extracting Widevine CDM from image..." +msgstr "Estrazione di Widevine CDM dall'immagine..." + # DNS start [ settings and declaration ] msgctxt "#707401" msgid "Enable DNS check alert"