def get_item(self, driveid, item_driveid=None, item_id=None, path=None, find_subtitles=False, include_download_info=False): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) cache_key = self._addonid+'-drive-'+driveid+'-item_driveid-'+Utils.str(item_driveid)+'-item_id-'+Utils.str(item_id)+'-path-'+Utils.str(path) f = self._cache.get(cache_key) if f: item = self._extract_item(f, include_download_info) else: cache_key = self._addonid+'-drive-None-item_driveid-None-item_id-None-path-'+path f = self._cache.get(cache_key) if f: item = self._extract_item(f, include_download_info) else: self._parameters['fields'] = self._file_fileds if not item_id and path == '/': item_id = 'root' if item_id: f = self._provider.get('/files/%s' % item_id, parameters = self._parameters) self._cache.set(cache_key, f, expiration=datetime.timedelta(seconds=59)) item = self._extract_item(f, include_download_info) else: item = self.get_item_by_path(path, include_download_info) if find_subtitles: subtitles = [] self._parameters['q'] = 'name contains \'%s\'' % urllib.quote(Utils.remove_extension(item['name'])) files = self._provider.get('/files', parameters = self._parameters) for f in files['files']: subtitle = self._extract_item(f, include_download_info) if subtitle['name_extension'] == 'srt' or subtitle['name_extension'] == 'sub' or subtitle['name_extension'] == 'sbv': subtitles.append(subtitle) if subtitles: item['subtitles'] = subtitles return item
def get_item(self, driveid, item_driveid=None, item_id=None, path=None, find_subtitles=False, include_download_info=False): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) cache_key = self._addonid+'-drive-'+driveid+'-item_driveid-'+Utils.str(item_driveid)+'-item_id-'+Utils.str(item_id)+'-path-'+Utils.str(path) f = self._cache.get(cache_key) if not f : if item_id: f = self._provider.get('/drives/'+item_driveid+'/items/' + item_id, parameters = self._extra_parameters) elif path == 'sharedWithMe' or path == 'recent': return else: if path == '/': path = 'root' else: parts = path.split('/') if len(parts) > 1 and not parts[0]: path = 'root:'+path+':' f = self._provider.get('/drives/'+driveid+'/' + path, parameters = self._extra_parameters) self._cache.set(cache_key, f, expiration=datetime.timedelta(seconds=59)) item = self._extract_item(f, include_download_info) if find_subtitles: subtitles = [] parent_id = Utils.get_safe_value(Utils.get_safe_value(f, 'parentReference', {}), 'id') search_url = '/drives/'+item_driveid+'/items/' + parent_id + '/search(q=\'{'+urllib.quote(Utils.remove_extension(item['name']))+'}\')' files = self._provider.get(search_url) for f in files['value']: subtitle = self._extract_item(f, include_download_info) if subtitle['name_extension'] == 'srt' or subtitle['name_extension'] == 'sub' or subtitle['name_extension'] == 'sbv': subtitles.append(subtitle) if subtitles: item['subtitles'] = subtitles return item
def __init__(self): profile_path = Utils.unicode(KodiUtils.translate_path(KodiUtils.get_addon_info('profile'))) ini_path = os.path.join(profile_path, 'onedrive.ini') if os.path.exists(ini_path): config = ConfigParser() account_manager = AccountManager(profile_path) config.read(ini_path) for driveid in config.sections(): Logger.notice('Migrating drive %s...' % driveid) account = { 'id' : driveid, 'name' : config.get(driveid, 'name')} account['drives'] = [{ 'id' : driveid, 'name' : '', 'type' : 'migrated' }] account['access_tokens'] = { 'access_token': config.get(driveid, 'access_token'), 'refresh_token': config.get(driveid, 'refresh_token'), 'expires_in': 0, 'date': 0 } try: account_manager.add_account(account) except Exception as e: raise UIException(32021, e) os.remove(ini_path) KodiUtils.set_addon_setting('migrated', 'true')
def get_subtitles(self, parent, name, item_driveid=None, include_download_info=False): parameters = self.prepare_parameters() item_driveid = Utils.default(item_driveid, self._driveid) subtitles = [] parameters['fields'] = 'files(' + self._get_field_parameters() + ')' parameters['q'] = 'name contains \'%s\'' % Utils.str( Utils.remove_extension(name)).replace("'", "\\'") files = self.get('/files', parameters=parameters) for f in files['files']: subtitle = self._extract_item(f, include_download_info) if subtitle['name_extension'].lower() in ('srt', 'idx', 'sub', 'sbv', 'ass', 'ssa', 'smi'): subtitles.append(subtitle) return subtitles
def _show_progress_after_change(self, change, change_type, pending_changes, changes_done, retry_changes, export): completed = len(changes_done) + len(retry_changes) target = len(pending_changes) + completed msg = self._common_addon.getLocalizedString(32041) if change_type: msg = Utils.get_safe_value(change, 'name', 'n/a') self._export_progress_dialog_bg.update( self._get_percent(completed, target), self._addon_name + ' ' + self._get_progress_header(export), msg)
def search(self, query, item_driveid=None, item_id=None, on_items_page_completed=None): item_driveid = Utils.default(item_driveid, self._driveid) parameters = self.prepare_parameters() parameters[ 'fields'] = 'files(%s),kind,nextPageToken' % self._get_field_parameters( ) query = 'fullText contains \'%s\'' % Utils.str(query) if item_id: query += ' and \'%s\' in parents' % item_id parameters['q'] = query + ' and not trashed' parameters['pageSize'] = 1000 files = self.get('/files', parameters=parameters) if self.cancel_operation(): return return self.process_files(files, parameters, on_items_page_completed)
def _slideshow(self, driveid, item_driveid=None, item_id=None, path=None, change_token=None): new_change_token = self.new_change_token_slideshow( change_token, driveid, item_driveid, item_id, path) if self.cancel_operation(): return wait_for_slideshow = False if not change_token or change_token != new_change_token: Logger.notice( 'Slideshow will start. change_token: %s, new_change_token: %s' % (change_token, new_change_token)) params = { 'action': '_list_folder', 'content_type': self._content_type, 'item_driveid': Utils.default(item_driveid, ''), 'item_id': Utils.default(item_id, ''), 'driveid': driveid, 'path': Utils.default(path, '') } extra_params = ',recursive' if self._addon.getSetting( 'slideshow_recursive') == 'true' else '' KodiUtils.executebuiltin('SlideShow(' + self._addon_url + '?' + urllib.urlencode(params) + extra_params + ')') wait_for_slideshow = True else: Logger.notice( 'Slideshow child count is the same, nothing to refresh...') t = threading.Thread(target=self._refresh_slideshow, args=( driveid, item_driveid, item_id, path, new_change_token, wait_for_slideshow, )) t.setDaemon(True) t.start()
def get_subtitles(self, driveid, path): item = self.get_item(driveid, path) key = '%s%s-subtitles' % ( driveid, path, ) Logger.debug('Testing subtitles from cache: %s' % key) subtitles = self._items_cache.get(key) if not subtitles: provider = self._get_provider() provider.configure(self._account_manager, driveid) self.is_path_possible(driveid, path) item_driveid = Utils.default( Utils.get_safe_value(item, 'drive_id'), driveid) subtitles = provider.get_subtitles(item['parent'], item['name'], item_driveid) Logger.debug('Saving subtitles in cache: %s' % key) self._items_cache.set(key, item) return subtitles
def process_files(self, files, parameters, on_items_page_completed=None, include_download_info=False, extra_info=None): items = [] if files: kind = Utils.get_safe_value(files, 'kind', '') collection = [] if kind == 'drive#fileList': collection = files['files'] elif kind == 'drive#changeList': collection = files['changes'] elif 'albums' in files: kind = 'album' collection = files['albums'] elif 'mediaItems' in files: kind = 'media_item' collection = files['mediaItems'] if collection: for f in collection: f['kind'] = Utils.get_safe_value(f, 'kind', kind) item = self._extract_item(f, include_download_info) if item: items.append(item) if on_items_page_completed: on_items_page_completed(items) if type(extra_info) is dict: if 'newStartPageToken' in files: extra_info['change_token'] = files['newStartPageToken'] if 'nextPageToken' in files: parameters['pageToken'] = files['nextPageToken'] url = '/files' provider_method = self.get if kind == 'drive#changeList': url = '/changes' elif kind == 'album': url = '/albums' provider_method = self._photos_provider.get elif kind == 'media_item': url = '/mediaItems:search' provider_method = self._photos_provider.post next_files = provider_method(url, parameters = parameters) if self.cancel_operation(): return items.extend(self.process_files(next_files, parameters, on_items_page_completed, include_download_info, extra_info)) return items
def _select_stream_format(self, driveid, item_driveid=None, item_id=None, auto=False): url = None if not auto: self._progress_dialog.update(0, self._addon.getLocalizedString(32009)) self._provider.configure(self._account_manager, driveid) self._provider.get_item(item_driveid, item_id) request = Request('https://drive.google.com/get_video_info', urllib.urlencode({'docid' : item_id}), {'authorization': 'Bearer %s' % self._provider.get_access_tokens()['access_token']}) response_text = request.request() response_params = dict(urlparse.parse_qsl(response_text)) if not auto: self._progress_dialog.close() if Utils.get_safe_value(response_params, 'status', '') == 'ok': fmt_list = Utils.get_safe_value(response_params, 'fmt_list', '').split(',') stream_formats = [] for fmt in fmt_list: data = fmt.split('/') stream_formats.append(data[1]) stream_formats.append(self._addon.getLocalizedString(32015)) Logger.debug('Stream formats: %s' % Utils.str(stream_formats)) select = -1 if auto: select = self._auto_select_stream(stream_formats) else: select = self._dialog.select(self._addon.getLocalizedString(32016), stream_formats, 8000, 0) Logger.debug('Selected: %s' % Utils.str(select)) if select == -1: self._cancel_operation = True elif select != len(stream_formats) - 1: data = fmt_list[select].split('/') fmt_stream_map = Utils.get_safe_value(response_params, 'fmt_stream_map', '').split(',') for fmt in fmt_stream_map: stream_data = fmt.split('|') if stream_data[0] == data[0]: url = stream_data[1] break if url: cookie_header = '' for cookie in request.response_cookies: if cookie_header: cookie_header += ';' cookie_header += cookie.name + '=' + cookie.value; url += '|cookie=' + urllib.quote(cookie_header) return url
def handle_exception(ex): stacktrace = ExceptionUtils.full_stacktrace(ex) rex = ExceptionUtils.extract_exception(ex, RequestException) httpex = ExceptionUtils.extract_exception(ex, HTTPError) dnf = ExceptionUtils.extract_exception(ex, DriveNotFoundException) line1 = '' line2 = Utils.unicode(ex) send_report = True log_report = True if rex and rex.response: line1 = Utils.unicode(rex) line2 = ExceptionUtils.extract_error_message(rex.response) if httpex: if httpex.code == 401: send_report = False elif httpex.code == 404: send_report = False log_report = False if dnf: send_report = False log_report = False addonid = KodiUtils.get_addon_info('id') addon_version = KodiUtils.get_addon_info('version') common_addon_version = KodiUtils.get_addon_info( 'version', 'script.module.clouddrive.common') report = '[%s] [%s]/[%s]\n\n%s\n%s\n%s\n\n%s' % ( addonid, addon_version, common_addon_version, line1, line2, '', stacktrace) if rex: report += '\n\n%s\nResponse:\n%s' % (rex.request, rex.response) if log_report: Logger.debug(report) else: Logger.debug(ex) if send_report: Logger.notice(report) Logger.notice('Report sent') ErrorReport.send_report(report)
def _extract_item(self, f, driveid, include_download_info=False): size = long('%s' % Utils.get_safe_value(f, 'size', 0)) item = { 'id': f['id'], 'name': f['name'], 'name_extension': Utils.get_extension(f['name']), 'drive_id': driveid, 'path_lower': f['path_lower'], 'last_modified_date': Utils.get_safe_value(f, 'client_modified'), 'size': size } if f['.tag'] == 'folder': item['folder'] = {'child_count': 0} metadata = Utils.get_safe_value( Utils.get_safe_value(f, 'media_info', {}), 'metadata', {}) tag = Utils.get_safe_value(metadata, '.tag', '') if tag == 'video': video = metadata['video'] dimensions = Utils.get_safe_value(video, 'dimensions', {}) item['video'] = { 'width': long('%s' % Utils.get_safe_value(dimensions, 'width', 0)), 'height': long('%s' % Utils.get_safe_value(dimensions, 'height', 0)), 'duration': long('%s' % Utils.get_safe_value(video, 'duration', 0)) / 1000 } elif tag == 'photo': item['image'] = {'size': size} if include_download_info: parameters = { 'arg': json.dumps({'path': item['id']}), 'authorization': 'Bearer %s' % self._provider.get_access_tokens()['access_token'] } url = self._provider._get_content_url() + '/files/download' item['download_info'] = { 'url': url + '?%s' % urllib.urlencode(parameters) } return item
def get_export_map(self): exports = self.export_manager.load() export_map = {} for exportid in exports: export = exports[exportid] schedules = Utils.get_safe_value(export, 'schedules', []) exporting = Utils.get_safe_value(export, 'exporting', False) if not exporting: if Utils.get_safe_value(export, 'schedule', False) and schedules: for schedule in schedules: key = Utils.str(Utils.get_safe_value(schedule, 'type', '')) if key != self._startup_type: key += Utils.get_safe_value(schedule, 'at', '') export_map[key] = Utils.get_safe_value(export_map, key, []) export_map[key].append(export) if Utils.get_safe_value(export, 'watch', False): export_map['watch'] = Utils.get_safe_value(export_map, 'watch', []) export_map['watch'].append(export) Logger.debug('export_map: %s' % Utils.str(export_map)) return export_map
def get_item_by_path(self, path, include_download_info=False): parameters = self.prepare_parameters() if path[-1:] == '/': path = path[:-1] Logger.debug(path + ' <- Target') key = '%s%s' % ( self._driveid, path, ) Logger.debug('Testing item from cache: %s' % key) item = self._items_cache.get(key) if not item: parameters['fields'] = 'files(%s)' % self._get_field_parameters() index = path.rfind('/') filename = urllib.unquote(path[index + 1:]) parent = path[0:index] if not parent: parent = 'root' else: parent = self.get_item_by_path(parent, include_download_info)['id'] item = None parameters['q'] = '\'%s\' in parents and name = \'%s\'' % ( Utils.str(parent), Utils.str(filename).replace("'", "\\'")) parameters['pageSize'] = 1000 files = self.get('/files', parameters=parameters) if (len(files['files']) > 0): for f in files['files']: item = self._extract_item(f, include_download_info) break else: Logger.debug('Found in cache.') if not item: raise RequestException( 'Not found by path', HTTPError(path, 404, 'Not found', None, None), 'Request URL: %s' % path, None) else: self._items_cache.set(key, item) return item
def onInit(self): self.title_label = self.getControl(1000) self.cancel_button = self.getControl(999) self.save_button = self.getControl(1001) self.save_export_button = self.getControl(1002) self.drive_name_label = self.getControl(1003) self.drive_folder_label = self.getControl(1004) self.dest_folder_label = self.getControl(1005) self.dest_folder_button = self.getControl(1006) self.update_library_sw = self.getControl(1007) self.download_artwork_sw = self.getControl(1008) self.watch_drive_sw = self.getControl(1009) self.schedule_sw = self.getControl(1010) self.schedule_label = self.getControl(10100) self.schedule_list = self.getControl(1011) self.add_schedule_button = self.getControl(1012) self.setFocus(self.dest_folder_button) self.schedule_label.setLabel( self._common_addon.getLocalizedString(32083)) self.title_label.setLabel(self._addon_name + ' - ' + self._common_addon.getLocalizedString(32004)) account = self.account_manager.get_by_driveid('account', self.driveid) drive = self.account_manager.get_by_driveid('drive', self.driveid, account) drive_name = self.account_manager.get_account_display_name( account, drive, self.provider, True) self.drive_name_label.setLabel(drive_name) self.drive_folder_label.setLabel(self.name) exports = self.export_manager.get_exports() export = Utils.get_safe_value(exports, self.item_id, {}) if export: self.editing = True self.watch_drive_sw.setSelected( Utils.get_safe_value(export, 'watch', False)) self.download_artwork_sw.setSelected( Utils.get_safe_value(export, 'download_artwork', False)) self.schedule_sw.setSelected( Utils.get_safe_value(export, 'schedule', False)) self.update_library_sw.setSelected( Utils.get_safe_value(export, 'update_library', False)) self.schedules = Utils.get_safe_value(export, 'schedules', []) for schedule in self.schedules: self.add_schedule_item(schedule) if not self.editing: self.select_detination() else: self.dest_folder_label.setLabel( Utils.get_safe_value(export, 'destination_folder', '')) self.schedule_enabled(self.schedule_sw.isSelected())
def import_bookmarks(self, driveid, item_driveid=None, item_id=None): self._provider.configure(self._account_manager, driveid) # self._progress_dialog.update(0, '') # color = 'lime' # ban = self._common_addon.getLocalizedString(32013) item = self._provider.get_item(item_driveid=item_driveid, item_id=item_id, find_bookmarks=True, include_download_info=True) bookmarks = [] for bookmark in item['bookmarks']: bookmarks.append( self._get_item_play_url( urllib.quote(Utils.str(bookmark['name'])), driveid, Utils.default(Utils.get_safe_value(bookmark, 'drive_id'), driveid), bookmark['id'], True)) url = bookmark['download_info']['url'] request_params = { 'on_complete': lambda request: self.save_bookmark_image(request.response_text ), } request = self._provider.prepare_request('get', url) response = request.request() if request.response_code == 403 or request.response_code == 429: color = 'red' ban = self._common_addon.getLocalizedString(32033) else: self.save_bookmark_image(response, bookmark["name"]) item_path = 'plugin://plugin.googledrive/?item_id=%s&driveid=%s&item_driveid=%s&action=play&content_type=video' % ( item_id, driveid, item_driveid) p = { 'mode': 'import_image', 'item_label': item["name"], 'image_name': bookmark["name"], 'item_path': item_path, } addon_id = 'context.item.savebookmarks' KodiUtils.run_plugin(addon_id, p)
def _remove_export(self, driveid, item_id): self._export_manager = ExportManager(self._profile_path) exports = self._export_manager.get_exports() if item_id in exports: item = exports[item_id] remove_export = self._dialog.yesno( self._addon_name, self._common_addon.getLocalizedString(32001) % Utils.unicode(item['name'])) if remove_export: keep_locals = self._dialog.yesno( self._addon_name, self._common_addon.getLocalizedString(32086) % Utils.unicode(item['name'])) if not keep_locals: self._export_manager.remove_export(item_id, False) else: self._export_manager.remove_export(item_id) KodiUtils.executebuiltin('Container.Refresh') else: KodiUtils.executebuiltin('Container.Refresh')
def changes(self): change_token = self.get_change_token() if not change_token: change_token = Utils.get_safe_value( self.get('/changes/startPageToken', parameters=self.prepare_parameters()), 'startPageToken') extra_info = {} parameters = self.prepare_parameters() parameters['pageToken'] = change_token parameters[ 'fields'] = 'kind,nextPageToken,newStartPageToken,changes(kind,type,removed,file(%s))' % self._get_field_parameters( ) f = self.get('/changes', parameters=parameters) changes = self.process_files(f, parameters, include_download_info=True, extra_info=extra_info) self.persist_change_token( Utils.get_safe_value(extra_info, 'change_token')) return changes
def get_folder_items(self, item_driveid=None, item_id=None, path=None, on_items_page_completed=None, include_download_info=False): item_driveid = Utils.default(item_driveid, self._driveid) is_album = item_id and item_id[:6] == 'album-' if is_album: item_id = item_id[6:] parameters = self.prepare_parameters() if item_id: parameters['q'] = '\'%s\' in parents' % item_id elif path == 'sharedWithMe' or path == 'starred': parameters['q'] = path elif path != 'photos': if path == '/': parent = self._driveid if self._is_team_drive else 'root' parameters['q'] = '\'%s\' in parents' % parent elif not is_album: item = self.get_item_by_path(path, include_download_info) parameters['q'] = '\'%s\' in parents' % item['id'] parameters[ 'fields'] = 'files(%s),kind,nextPageToken' % self._get_field_parameters( ) if 'q' in parameters: parameters['q'] += ' and not trashed' self.configure(self._account_manager, self._driveid) provider_method = self.get url = '/files' parameters['pageSize'] = 1000 if path == 'photos': self._photos_provider = GooglePhotos() self._photos_provider.configure(self._account_manager, self._driveid) parameters = {} provider_method = self._photos_provider.get url = '/albums' elif is_album: self._photos_provider = GooglePhotos() self._photos_provider.configure(self._account_manager, self._driveid) parameters = {'albumId': item_id} provider_method = self._photos_provider.post url = '/mediaItems:search' files = provider_method(url, parameters=parameters) if self.cancel_operation(): return return self.process_files(files, parameters, on_items_page_completed, include_download_info)
def search(self, query, driveid, item_driveid=None, item_id=None, on_items_page_completed=None): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) self._parameters['fields'] = 'files(%s)' % self._file_fileds query = 'fullText contains \'%s\'' % query if item_id: query += ' and \'%s\' in parents' % item_id self._parameters['q'] = query files = self._provider.get('/files', parameters = self._parameters) if self.cancel_operation(): return return self.process_files(driveid, files, on_items_page_completed)
def refresh_tokens(self, provider_name, refresh_token, request_params=None): request_params = Utils.default(request_params, {}) headers = {'addon': self.get_addon_header()} body = urllib.parse.urlencode({ 'provider': provider_name, 'refresh_token': refresh_token }) return Request(KodiUtils.get_signin_server() + '/refresh', body, headers, **request_params).request_json()
def create_text_file(file_path, content): f = None try: f = KodiUtils.file(file_path, 'w') f.write(Utils.str(content)) except Exception as e: ErrorReport.handle_exception(e) return False finally: if f: f.close() return True
def display_google_ban_result(self, request): self._progress_dialog.close() color = 'lime' ban = self._common_addon.getLocalizedString(32013) if request.response_code == 403 or request.response_code == 429: color = 'red' ban = self._common_addon.getLocalizedString(32033) self._dialog.ok(self._addon_name, self._addon.getLocalizedString(32072) % '[B][COLOR %s]%s[/COLOR][/B]' % (color, ban,), self._addon.getLocalizedString(32073) % '[B]%s[/B]' % Utils.str(request.response_code), request.response_text )
def new_change_token_slideshow(self, change_token, driveid, item_driveid=None, item_id=None, path=None): self._provider.configure(self._account_manager, driveid) if not change_token: response = self._provider.get( '/changes/startPageToken', parameters=self._provider._parameters) self._change_token = Utils.get_safe_value(response, 'startPageToken') change_token = 1 else: page_token = self._change_token while page_token: self._provider._parameters['pageToken'] = page_token self._provider._parameters[ 'fields'] = 'nextPageToken,newStartPageToken,changes(file(id,name,parents))' response = self._provider.get( '/changes', parameters=self._provider._parameters) if self.cancel_operation(): return self._change_token = Utils.get_safe_value( response, 'newStartPageToken', self._change_token) changes = Utils.get_safe_value(response, 'changes', []) for change in changes: f = Utils.get_safe_value(change, 'file', {}) parents = Utils.get_safe_value(f, 'parents', []) parents.append(f['id']) if item_id in parents: return change_token + 1 page_token = Utils.get_safe_value(response, 'nextPageToken') return change_token
def play(self, driveid, item_driveid=None, item_id=None): find_subtitles = self._addon.getSetting( 'set_subtitle') == 'true' and self._content_type == 'video' item = self.get_item(driveid, item_driveid, item_id, find_subtitles=find_subtitles) file_name = Utils.unicode(item['name']) list_item = xbmcgui.ListItem(file_name) if 'audio' in item: list_item.setInfo('music', item['audio']) elif 'video' in item: list_item.addStreamInfo('video', item['video']) list_item.select(True) list_item.setPath( DownloadServiceUtil.build_download_url( self._addonid, driveid, item_driveid, item_id, urllib.quote(Utils.str(file_name)))) list_item.setProperty('mimetype', Utils.get_safe_value(item, 'mimetype')) if find_subtitles and 'subtitles' in item: subtitles = [] for subtitle in item['subtitles']: subtitles.append( DownloadServiceUtil.build_download_url( self._addonid, driveid, Utils.default( Utils.get_safe_value(subtitle, 'drive_id'), driveid), subtitle['id'], urllib.quote(Utils.str(subtitle['name'])))) list_item.setSubtitles(subtitles) xbmcplugin.setResolvedUrl(self._addon_handle, True, list_item)
def get_subtitles(self): try: from clouddrive.common.remote.request import Request from clouddrive.common.service.download import DownloadServiceUtil response = Request(self.getPlayingFile() + '?subtitles', None).request_json() if response and 'driveid' in response and 'subtitles' in response: driveid = response['driveid'] subtitles = response['subtitles'] for subtitle in subtitles: url = DownloadServiceUtil.build_download_url( driveid, Utils.default( Utils.get_safe_value(subtitle, 'drive_id'), driveid), subtitle['id'], urllib.quote(Utils.str(subtitle['name']))) Logger.debug('subtitle: %s' % url) self.setSubtitles(url) except Exception as e: Logger.error(e) from clouddrive.common.remote.errorreport import ErrorReport ErrorReport.handle_exception(e)
def prepare_request(self, method, path, parameters=None, request_params=None, access_tokens=None, headers=None): parameters = Utils.default(parameters, {}) access_tokens = Utils.default(access_tokens, {}) encoded_parameters = urllib.urlencode(parameters) url = self._build_url(method, path, encoded_parameters) request_params = self._wrap_on_exception(request_params) if not headers: headers = Utils.default(self._get_request_headers(), {}) content_type = Utils.get_safe_value(headers, 'content-type', '') if content_type == 'application/json': data = json.dumps(parameters) else: data = None if method == 'get' else encoded_parameters if not access_tokens: access_tokens = self.get_access_tokens() self._validate_access_tokens(access_tokens, url, data, headers) if time.time() > (access_tokens['date'] + access_tokens['expires_in'] - 600): access_tokens.update(self.refresh_access_tokens(request_params)) self._validate_access_tokens(access_tokens, 'refresh_access_tokens', 'Unknown', 'Unknown') self.persist_access_tokens(access_tokens) headers['authorization'] = 'Bearer ' + access_tokens['access_token'] return Request(url, data, headers, **request_params)
def get_drives(self, request_params={}, access_tokens={}): response = self.get('/drives', request_params=request_params, access_tokens=access_tokens) drives = [] drives_id_list =[] for drive in response['value']: drives_id_list.append(drive['id']) drives.append({ 'id' : drive['id'], 'name' : Utils.get_safe_value(drive, 'name', ''), 'type' : drive['driveType'] }) response = self.get('/me/drives', request_params=request_params, access_tokens=access_tokens) for drive in response['value']: if not drive['id'] in drives_id_list: drives_id_list.append(drive['id']) drives.append({ 'id' : drive['id'], 'name' : Utils.get_safe_value(drive, 'name', ''), 'type' : drive['driveType'] }) return drives
def get_folder_items(self, item_driveid=None, item_id=None, path=None, on_items_page_completed=None, include_download_info=False): item_driveid = Utils.default(item_driveid, self._driveid) is_album = item_id and item_id[:6] == 'album-' if is_album: Logger.notice(item_id) item_id = item_id[6:] Logger.notice(item_id) parameters = copy.deepcopy(self._default_parameters) if item_id: parameters['q'] = '\'%s\' in parents' % item_id elif path == 'sharedWithMe' or path == 'starred': parameters['q'] = path elif path != 'photos': if path == '/': parameters['q'] = '\'root\' in parents' elif not is_album: item = self.get_item_by_path(path, include_download_info) parameters['q'] = '\'%s\' in parents' % item['id'] parameters['fields'] = 'files(%s),nextPageToken' % self._file_fileds if 'q' in parameters: parameters['q'] += ' and not trashed' if path == 'photos': self._photos_provider = GooglePhotos() Logger.notice(self._get_api_url()) self._photos_provider.configure(self._account_manager, self._driveid) files = self._photos_provider.get('/albums') files['is_album'] = True elif is_album: self._photos_provider = GooglePhotos() self._photos_provider.configure(self._account_manager, self._driveid) files = self._photos_provider.post('/mediaItems:search', parameters={'albumId': item_id}) files['is_media_items'] = True else: self.configure(self._account_manager, self._driveid) files = self.get('/files', parameters=parameters) files['is_album'] = False if self.cancel_operation(): return return self.process_files(files, parameters, on_items_page_completed, include_download_info)
def __init__(self): self._addon = KodiUtils.get_addon() self._addonid = self._addon.getAddonInfo('id') self._addon_name = self._addon.getAddonInfo('name') self._addon_url = sys.argv[0] self._addon_version = self._addon.getAddonInfo('version') self._common_addon_id = 'script.module.clouddrive.common' self._common_addon = KodiUtils.get_addon(self._common_addon_id) self._common_addon_version = self._common_addon.getAddonInfo('version') self._dialog = xbmcgui.Dialog() self._profile_path = Utils.unicode(KodiUtils.translate_path(self._addon.getAddonInfo('profile'))) self._progress_dialog = DialogProgress(self._addon_name) self._progress_dialog_bg = DialogProgressBG(self._addon_name) self._export_progress_dialog_bg = DialogProgressBG(self._addon_name) self._system_monitor = KodiUtils.get_system_monitor() self._account_manager = AccountManager(self._profile_path) self._pin_dialog = None self.iskrypton = KodiUtils.get_home_property('iskrypton') == 'true' if len(sys.argv) > 1: self._addon_handle = int(sys.argv[1]) self._addon_params = urlparse.parse_qs(sys.argv[2][1:]) for param in self._addon_params: self._addon_params[param] = self._addon_params.get(param)[0] self._content_type = Utils.get_safe_value(self._addon_params, 'content_type') if not self._content_type: wid = xbmcgui.getCurrentWindowId() if wid == 10005 or wid == 10500 or wid == 10501 or wid == 10502: self._content_type = 'audio' elif wid == 10002: self._content_type = 'image' else: self._content_type = 'video' xbmcplugin.addSortMethod(handle=self._addon_handle, sortMethod=xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.addSortMethod(handle=self._addon_handle, sortMethod=xbmcplugin.SORT_METHOD_UNSORTED ) xbmcplugin.addSortMethod(handle=self._addon_handle, sortMethod=xbmcplugin.SORT_METHOD_SIZE ) xbmcplugin.addSortMethod(handle=self._addon_handle, sortMethod=xbmcplugin.SORT_METHOD_DATE ) xbmcplugin.addSortMethod(handle=self._addon_handle, sortMethod=xbmcplugin.SORT_METHOD_DURATION )
def _refresh_slideshow(self, driveid, item_driveid, item_id, path, change_token, wait_for_slideshow): if wait_for_slideshow: Logger.notice( 'Waiting up to 10 minutes until the slideshow for folder %s starts...' % Utils.default(item_id, path)) max_waiting_time = time.time() + 10 * 60 while not self.cancel_operation( ) and not KodiUtils.get_cond_visibility( 'Slideshow.IsActive') and max_waiting_time > time.time(): if self._system_monitor.waitForAbort(2): break self._print_slideshow_info() interval = self._addon.getSetting('slideshow_refresh_interval') Logger.notice( 'Waiting up to %s minute(s) to check if it is needed to refresh the slideshow of folder %s...' % (interval, Utils.default(item_id, path))) target_time = time.time() + int(interval) * 60 while not self.cancel_operation() and target_time > time.time( ) and KodiUtils.get_cond_visibility('Slideshow.IsActive'): if self._system_monitor.waitForAbort(10): break self._print_slideshow_info() if not self.cancel_operation() and KodiUtils.get_cond_visibility( 'Slideshow.IsActive'): try: self._slideshow(driveid, item_driveid, item_id, path, change_token) except Exception as e: Logger.error( 'Slideshow failed to auto refresh. Will be restarted when possible. Error: ' ) Logger.error(ExceptionUtils.full_stacktrace(e)) self._refresh_slideshow(driveid, item_driveid, item_id, path, None, wait_for_slideshow) else: Logger.notice( 'Slideshow is not running anymore or abort requested.')
def onInit(self): import pyqrcode self._image_path = os.path.join( Utils.unicode( KodiUtils.translate_path( KodiUtils.get_addon_info( "profile", "script.module.clouddrive.common"))), "qr.png") qrcode = pyqrcode.create(self.qr_code) qrcode.png(self._image_path, scale=10) del qrcode self.getControl(self._heading_control).setLabel(self.heading) self.getControl(self._qr_control).setImage(self._image_path) self.update(self.percent, self.line1, self.line2, self.line3)
def create_strm(driveid, item, file_path, content_type, addon_url): item_id = Utils.str(item['id']) item_drive_id = Utils.default(Utils.get_safe_value(item, 'drive_id'), driveid) f = None try: f = KodiUtils.file(file_path, 'w') content = addon_url + '?' + urllib.urlencode({ 'action': 'play', 'content_type': content_type, 'item_driveid': item_drive_id, 'item_id': item_id, 'driveid': driveid }) if item['name_extension'] == 'strm': content = Request(item['download_info']['url'], None).request() f.write(content) except: return False finally: if f: f.close() return True
def search(self, query, driveid, item_driveid=None, item_id=None, on_items_page_completed=None): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) url = '/drives/' if item_id: url += item_driveid+'/items/' + item_id else: url += driveid url += '/search(q=\''+urllib.quote(query)+'\')' self._extra_parameters['filter'] = 'file ne null' files = self._provider.get(url, parameters = self._extra_parameters) if self.cancel_operation(): return return self.process_files(driveid, files, on_items_page_completed)
def process_path(self, addon_name, drive_name, path): headers = {} response = Utils.get_file_buffer() driveid = self.get_driveid(drive_name) if driveid: parts = self.path.split('/') filename = parts[len(parts) - 1] if filename: response_code = 303 if path: u = urlparse(path) path = u.path Logger.debug('query: %s' % u.query) if u.query == 'subtitles': response_code = 200 response.write( json.dumps({ 'driveid': driveid, 'subtitles': self.get_subtitles(driveid, path) })) else: key = '%s%s:children' % ( driveid, path[0:path.rfind('/')], ) Logger.debug('reading cache key: ' + key) children = self._children_cache.get(key) if not children and type(children) is NoneType: self.get_folder_items(driveid, path[0:path.rfind('/') + 1]) url = self.get_download_url(driveid, path) headers['location'] = url Logger.debug('redirect to: ' + url) else: url = self.path + '/' headers['location'] = url else: response_code = 200 response.write(str(self.show_folder(driveid, path))) else: response_code = 404 response.write('Drive "%s" does not exist for addon "%s"' % (drive_name, addon_name)) return { 'response_code': response_code, 'content': response, 'headers': headers }
def __export_folder(self, driveid, folder, export_folder, export, items_info): folder_id = Utils.str(folder['id']) folder_name = Utils.unicode(folder['name']) folder_path = os.path.join(os.path.join(export_folder, folder_name), '') if not xbmcvfs.exists(folder_path): try: xbmcvfs.mkdirs(folder_path) except: if self._system_monitor.waitForAbort(3): return xbmcvfs.mkdirs(folder_path) items = self.get_provider().get_folder_items( Utils.default(Utils.get_safe_value(folder, 'drive_id'), driveid), folder['id']) if self.cancel_operation(): return for item in items: if 'folder' in item: if self._child_count_supported: self._exporting_target += int( item['folder']['child_count']) else: self._exporting_target += 1 for item in items: is_folder = 'folder' in item item_id = Utils.str(item['id']) item_name = Utils.unicode(item['name']) item_name_extension = item['name_extension'] file_path = os.path.join(folder_path, item_name) if is_folder: ExportManager.add_item_info(items_info, item_id, item_name, os.path.join(file_path, ''), folder_id) self.__export_folder(driveid, item, folder_path, export, items_info) elif (('video' in item or item_name_extension in self._video_file_extensions) and export['content_type'] == 'video') or ( 'audio' in item and export['content_type'] == 'audio'): item_name += ExportManager._strm_extension file_path += ExportManager._strm_extension ExportManager.create_strm(driveid, item, file_path, export['content_type'], self._addon_url) ExportManager.add_item_info(items_info, item_id, item_name, file_path, folder_id) self._exporting_count += 1 p = int(self._exporting_count / float(self._exporting_target) * 100) if self._exporting_percent < p: self._exporting_percent = p self._export_progress_dialog_bg.update( self._exporting_percent, self._addon_name + ' ' + self._common_addon.getLocalizedString(32024), file_path[len(export['destination_folder']):])
def process_files(self, driveid, files, on_items_page_completed=None): items = [] for f in files['value']: f = Utils.get_safe_value(f, 'remoteItem', f) item = self._extract_item(f) cache_key = self._addonid+'-drive-'+driveid+'-item_driveid-'+item['drive_id']+'-item_id-'+item['id']+'-path-None' self._cache.set(cache_key, f, expiration=datetime.timedelta(minutes=1)) items.append(item) if on_items_page_completed: on_items_page_completed(items) if '@odata.nextLink' in files: next_files = self._provider.get(files['@odata.nextLink']) if self.cancel_operation(): return items.extend(self.process_files(driveid, next_files, on_items_page_completed)) return items
def get_folder_items(self, driveid, item_driveid=None, item_id=None, path=None, on_items_page_completed=None): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) if item_id: files = self._provider.get('/drives/'+item_driveid+'/items/' + item_id + '/children', parameters = self._extra_parameters) elif path == 'sharedWithMe' or path == 'recent': files = self._provider.get('/drives/'+driveid+'/' + path) else: if path == '/': path = 'root' else: parts = path.split('/') if len(parts) > 1 and not parts[0]: path = 'root:'+path+':' files = self._provider.get('/drives/'+driveid+'/' + path + '/children', parameters = self._extra_parameters) if self.cancel_operation(): return return self.process_files(driveid, files, on_items_page_completed)
def get_folder_items(self, driveid, item_driveid=None, item_id=None, path=None, on_items_page_completed=None): self._provider.configure(self._account_manager, driveid) item_driveid = Utils.default(item_driveid, driveid) self._parameters['fields'] = 'files(%s),nextPageToken' % self._file_fileds if item_id: self._parameters['q'] = '\'%s\' in parents' % item_id elif path == 'sharedWithMe' or path == 'starred': self._parameters['q'] = path elif path == 'photos': self._parameters['spaces'] = path else: if path == '/': self._parameters['q'] = '\'root\' in parents' else: item = self.get_item_by_path(path) self._parameters['q'] = '\'%s\' in parents' % item['id'] files = self._provider.get('/files', parameters = self._parameters) if self.cancel_operation(): return return self.process_files(driveid, files, on_items_page_completed)
def new_change_token_slideshow(self, change_token, driveid, item_driveid=None, item_id=None, path=None): self._provider.configure(self._account_manager, driveid) if not change_token: response = self._provider.get('/changes/startPageToken', parameters = self._parameters) self._change_token = Utils.get_safe_value(response, 'startPageToken') change_token = 1 else: page_token = self._change_token while page_token: self._parameters['pageToken'] = page_token self._parameters['fields'] = 'nextPageToken,newStartPageToken,changes(file(id,name,parents))' response = self._provider.get('/changes', parameters = self._parameters) if self.cancel_operation(): return self._change_token = Utils.get_safe_value(response, 'newStartPageToken', self._change_token) changes = Utils.get_safe_value(response, 'changes', []) for change in changes: f = Utils.get_safe_value(change, 'file', {}) parents = Utils.get_safe_value(f, 'parents', []) parents.append(f['id']) if item_id in parents: return change_token + 1 page_token = Utils.get_safe_value(response, 'nextPageToken') return change_token
def _extract_item(self, f, include_download_info=False): item = { 'id': f['id'], 'name': f['name'], 'name_extension' : Utils.get_extension(f['name']), 'drive_id' : Utils.get_safe_value(Utils.get_safe_value(f, 'parentReference', {}), 'driveId'), 'mimetype' : Utils.get_safe_value(Utils.get_safe_value(f, 'file', {}), 'mimeType'), 'last_modified_date' : Utils.get_safe_value(f,'lastModifiedDateTime'), 'size': Utils.get_safe_value(f, 'size', 0), 'description': Utils.get_safe_value(f, 'description', '') } if 'folder' in f: item['folder'] = { 'child_count' : Utils.get_safe_value(f['folder'],'childCount',0) } if 'video' in f: video = f['video'] item['video'] = { 'width' : Utils.get_safe_value(video,'width', 0), 'height' : Utils.get_safe_value(video, 'height', 0), 'duration' : Utils.get_safe_value(video, 'duration', 0) /1000 } if 'audio' in f: audio = f['audio'] item['audio'] = { 'tracknumber' : Utils.get_safe_value(audio, 'track'), 'discnumber' : Utils.get_safe_value(audio, 'disc'), 'duration' : int(Utils.get_safe_value(audio, 'duration') or '0') / 1000, 'year' : Utils.get_safe_value(audio, 'year'), 'genre' : Utils.get_safe_value(audio, 'genre'), 'album': Utils.get_safe_value(audio, 'album'), 'artist': Utils.get_safe_value(audio, 'artist'), 'title': Utils.get_safe_value(audio, 'title') } if 'image' in f or 'photo' in f: item['image'] = { 'size' : Utils.get_safe_value(f, 'size', 0) } if 'thumbnails' in f and type(f['thumbnails']) == list and len(f['thumbnails']) > 0: thumbnails = f['thumbnails'][0] item['thumbnail'] = Utils.get_safe_value(Utils.get_safe_value(thumbnails, 'large', {}), 'url', '') if include_download_info: item['download_info'] = { 'url' : Utils.get_safe_value(f,'@microsoft.graph.downloadUrl') } return item
def _rename_action(self): self._action = Utils.get_safe_value({ 'open_folder': '_list_folder', 'open_drive': '_list_drive', }, self._action, self._action)
def _extract_item(self, f, include_download_info=False): size = long('%s' % Utils.get_safe_value(f, 'size', 0)) item = { 'id': f['id'], 'name': f['name'], 'name_extension' : Utils.get_extension(f['name']), 'drive_id' : Utils.get_safe_value(Utils.get_safe_value(f, 'owners', [{}])[0], 'permissionId'), 'mimetype' : Utils.get_safe_value(f, 'mimeType', ''), 'last_modified_date' : Utils.get_safe_value(f,'modifiedTime'), 'size': size, 'description': Utils.get_safe_value(f, 'description', '') } if item['mimetype'] == 'application/vnd.google-apps.folder': item['folder'] = { 'child_count' : 0 } if 'videoMediaMetadata' in f: video = f['videoMediaMetadata'] item['video'] = { 'width' : Utils.get_safe_value(video, 'width'), 'height' : Utils.get_safe_value(video, 'height'), 'duration' : long('%s' % Utils.get_safe_value(video, 'durationMillis', 0)) / 1000 } if 'imageMediaMetadata' in f: item['image'] = { 'size' : size } if 'hasThumbnail' in f and f['hasThumbnail']: item['thumbnail'] = Utils.get_safe_value(f, 'thumbnailLink') if include_download_info: parameters = { 'alt': 'media', 'access_token': self._provider.get_access_tokens()['access_token'] } url = self._provider._get_api_url() + '/files/%s' % item['id'] if 'size' not in f and item['mimetype'] == 'application/vnd.google-apps.document': url += '/export' parameters['mimeType'] = self.get_mimetype_by_extension(item['name_extension']) item['download_info'] = { 'url' : url + '?%s' % urllib.urlencode(parameters) } return item