def makeListItem(self, replaceItems=False): colorItem = getSetting('item_default_color') colorPl = getSetting('item_section_color') label = self.get_label() image = self.get_image() owner = self.get_owner() url = self.make_url() if not self.is_my_playlist: label = '%s - %s' % (color(colorItem, owner), label) if self.b_is_current: fmt = getSetting('playlist_current_format') label = fmt % (color(colorPl, label)) item = xbmcgui.ListItem(label, owner, image, image, url) if not item: warn(self, "Error: Cannot make xbmc list item") return None item.setPath(url) ctxMenu = contextMenu() self.attach_context_menu(item, ctxMenu) item.addContextMenuItems(ctxMenu.getTuples(), replaceItems) return item
def get_checked_parameters(): """Parse parameters passed to xbmc plugin as sys.argv """ d = dog() rparam = {} if len(sys.argv) <= 1: return rparam paramstring = sys.argv[2] if len(paramstring) >= 2: params = sys.argv[2] cleanedparams = params.replace('?', '') if (params[len(params) - 1] == '/'): params = params[0:len(params) - 2] pairsofparams = cleanedparams.split('&') for i in range(len(pairsofparams)): splitparams = {} splitparams = pairsofparams[i].split('=') if (len(splitparams)) == 2: if d.kv_is_ok(splitparams[0], splitparams[1]): rparam[splitparams[0]] = splitparams[1] else: warn('[DOG]', "--- Invalid key: %s / value: %s" % (splitparams[0], splitparams[1])) return rparam
def remove(self): name = self.get_parameter('query') if name == 'qobuz.com': return False if not name: return False user = self.get_user_data() if not user: return False friends = user['player_settings'] if not 'friends' in friends: notifyH('Qobuz', "You don't have friend", 'icon-error-256') warn(self, "No friends in user/player_settings") return False friends = friends['friends'] if not name in friends: notifyH('Qobuz', "You're not friend with %s" % (name), 'icon-error-256') warn(self, "Friend " + repr(name) + " not in friends data") return False del friends[friends.index(name)] newdata = {'friends': friends} if not api.user_update(player_settings=json.dumps(newdata)): notifyH('Qobuz', 'Friend %s added' % (name)) notifyH('Qobuz', "Cannot updata friend's list...", 'icon-error-256') return False notifyH('Qobuz', 'Friend %s removed' % (name)) self.delete_cache() executeBuiltin(containerRefresh()) return True
def __add_pagination(self, data): '''build_down helper: Add pagination data when needed ''' if not data: return False items = None need_pagination = False for p in _paginated: if p not in data or data[p] is None: warn(self, 'No pagination data') continue items = data[p] if 'limit' not in items or 'total' not in items: continue if items['limit'] is None: continue if items['total'] > (items['offset'] + items['limit']): need_pagination = True break if need_pagination is False: return False url = self.make_url(offset=items['offset'] + items['limit']) self.pagination_next = url self.pagination_total = items['total'] self.pagination_offset = items['offset'] self.pagination_limit = items['limit'] self.pagination_next_offset = items['offset'] + items['limit'] return True
def _add_tracks(self, playlist_id, nodes): if len(nodes) < 1: warn(self, 'Empty list...') return False step = 50 start = 0 numtracks = len(nodes) if numtracks > 1000: notify_error('Qobuz', 'Max tracks per playlist reached (1000)' '\nSkipping %s tracks' % (numtracks - 1000)) numtracks = 1000 while start < numtracks: if (start + step) > numtracks: step = numtracks - start str_tracks = '' info(self, "Adding tracks start: %s, end: %s" % (start, start + step)) for i in range(start, start + step): node = nodes[i] if node.nt != Flag.TRACK: warn(self, "Not a Node_track node") continue str_tracks += '%s,' % (str(node.nid)) if not api.playlist_addTracks( playlist_id=playlist_id, track_ids=str_tracks): return False start += step return True
def fetch(self, Dir, lvl, whiteFlag, blackFlag): data = api.get('/album/get', album_id=self.nid) if not data: warn(self, "Cannot fetch product data") return False self.data = data return True
def scan(self): import sys from qobuz.node.flag import Flag """Building tree when using Xbmc library scanning feature """ from qobuz.gui.directory import Directory if not self.set_root_node(): warn(self, "Cannot set root node ('%s')" % (str( self.node_type))) return False handle = qobuz.boot.handle Dir = Directory(self.root, self.nodes, withProgress=False) Dir.handle = int(sys.argv[1]) Dir.asList = False Dir.asLocalURL = True if self.root.nt & Flag.TRACK: self.root.fetch(None, None, Flag.TRACK, Flag.NONE) Dir.add_node(self.root) else: self.root.populating(Dir, self.depth, self.whiteFlag, self.blackFlag) Dir.set_content(self.root.content_type) Dir.end_of_directory() notifyH('Scanning results', str(Dir.total_put) + ' items where scanned', mstime=3000) return True
def to_s(cls, flag): if not flag: warn(cls, "Missing flag parameter") return '' flag = int(flag) if flag & cls.TRACK == cls.TRACK: return "track" elif flag & cls.PLAYLIST == cls.PLAYLIST: return "playlist" elif flag & cls.USERPLAYLISTS == cls.USERPLAYLISTS: return "user_playlists" elif flag & cls.RECOMMENDATION == cls.RECOMMENDATION: return "recommendation" elif flag & cls.ROOT == cls.ROOT: return "root" elif flag & cls.ALBUM == cls.ALBUM: return "album" elif flag & cls.PURCHASES == cls.PURCHASES: return "purchases" elif flag & cls.PURCHASE == cls.PURCHASE: return "purchase" elif flag & cls.FAVORITES == cls.FAVORITES: return "favorites" elif flag & cls.FAVORITE == cls.FAVORITE: return "favorite" elif flag & cls.SEARCH == cls.SEARCH: return "search" elif flag & cls.ARTIST == cls.ARTIST: return "artist" elif flag & cls.SIMILAR_ARTIST == cls.SIMILAR_ARTIST: return "similar_artist" elif flag & cls.FRIEND == cls.FRIEND: return "friend" elif flag & cls.FRIENDS == cls.FRIENDS: return "friends" elif flag & cls.GENRE == cls.GENRE: return "genre" elif flag & cls.LABEL == cls.LABEL: return "label" elif flag & cls.NODE == cls.NODE: return "inode" elif flag & cls.STOPBUILD == cls.STOPBUILD: return "stop_build_down" elif flag & cls.ARTICLES == cls.ARTICLES: return "articles" elif flag & cls.ARTICLE == cls.ARTICLE: return "article" elif flag & cls.PUBLIC_PLAYLISTS == cls.PUBLIC_PLAYLISTS: return "public_playlists" elif flag & cls.ARTICLE_RUBRICS == cls.ARTICLE_RUBRICS: return "article_rubrics" elif flag & cls.ALBUMS_BY_ARTIST == cls.ALBUMS_BY_ARTIST: return "albums_by_artist" elif flag & cls.COLLECTION == cls.COLLECTION: return "collection" elif flag & cls.COLLECTIONS == cls.COLLECTIONS: return "collections" else: raise QobuzXbmcError( who=cls, what='invalid_flag', additional=repr(flag))
def mkdir(self, path): if not os.path.isdir(path): try: os.makedirs(path) except: warn("Cannot create directory: " + path) exit(2) info(self, "Directory created: " + path)
def get_streaming_url(self): data = self.__getFileUrl() if not data: return None if 'url' not in data: warn(self, "streaming_url, no url returned\n" "API Error: %s" % (api.error)) return None return data['url']
def fetch(self, Dir, lvl, whiteFlag, blackFlag): limit = getSetting('pagination_limit') data = api.get('/artist/getSimilarArtist', artist_id=self.nid, limit=limit, offset=self.offset, extra='albums') if not data: warn(self, "Cannot fetch albums for artist: " + self.get_label()) return False self.data = data return True
def fetch(self, Dir, lvl, whiteFlag, blackFlag): limit = getSetting('pagination_limit') data = api.get('/artist/get', artist_id=self.nid, limit=limit, offset=self.offset, extra='albums') if not data: warn(self, "Build-down: Cannot fetch artist data") return False self.data = data return True
def get_playlist_storage(self): if self.playlist_storage is not None: return self.playlist_storage if api.user_id is None or self.nid is None: warn(self, 'Missing user_id: {user_id} or nid: {nid}', user_id=api.user_id, nid=self.nid) return None self.playlist_storage = _Storage(self._get_playlist_storage_filename()) return self.playlist_storage
def get_year(self): import time date = self.get_property('released_at', default=None) year = 0 try: year = time.strftime("%Y", time.localtime(date)) except Exception: warn(self, 'Invalid date format %s', date) return year
def fetch(self, Dir, lvl, whiteFlag, blackFlag): limit = getSetting('pagination_limit') data = api.get('/purchase/getUserPurchases', limit=limit, offset=self.offset, user_id=api.user_id) if not data: warn(self, "Cannot fetch purchases data") return False self.data = data return True
def set_as_current(self, playlist_id=None): if not playlist_id: playlist_id = self.nid if not playlist_id: warn(self, 'Cannot set current playlist without id') return False userdata = self.get_user_storage() userdata['current_playlist'] = int(playlist_id) return userdata.sync()
def close(self): """Close our progress dialog """ if not self.is_enable: return True try: return super(Progress, self).close() except: warn(self, "Cannot close progress bar") return False
def fetch(self, Dir, lvl, whiteFlag, blackFlag): limit = getSetting('pagination_limit', asInt=True) data = api.get('/playlist/get', playlist_id=self.nid, offset=self.offset, limit=limit, extra='tracks') if not data: warn(self, "Build-down: Cannot fetch playlist data") return False self.data = data self.get_image() # Buld thumbnail if neeeded return True
def item_add_playing_property(self, item): """ We add this information only when playing item because it require us to fetch data from Qobuz """ mime = self.get_mimetype() if not mime: warn(self, "Cannot set item streaming url") return False item.setProperty('mimetype', mime) item.setPath(self.get_streaming_url()) return True
def fetch(self, Dir, lvl, whiteFlag, blackFlag): limit = getSetting('pagination_limit') data = api.get('/playlist/getUserPlaylists', limit=limit, offset=self.offset, user_id=api.user_id) if data is None: warn(self, "Build-down: Cannot fetch user playlists data") return False self.data = data return True
def iscanceled(self): """Return true if our dialog has been canceled by user """ if not self.is_enable: return False bs = True try: bs = super(Progress, self).iscanceled() except: warn(self, 'Cannot cancel progress...') return True return bs
def update_line1(self, line): """Only updating line1 """ if not line or line == self.line1: return False self.line1 = line try: return self.update(self.percent, self.line1, self.line2, self.line3) except: warn(self, "Cannot update line1 progress bar") return False
def get_year(self): import time date = self.get_property('album/released_at', default=None) if date is None: if self.parent is not None and self.parent.nt & Flag.ALBUM: return self.parent.get_year() year = 0 try: year = time.strftime("%Y", time.localtime(date)) except Exception as e: warn(self, 'Invalid date format %s', date) return year
def __init__(self, parent, params): super(Node_album, self).__init__(parent, params) self.nt = Flag.ALBUM self.image = getImage('album') self.content_type = 'songs' self.is_special_purchase = False self.imageDefaultSize = 'large' self.label = 'Album' self.offset = self.get_parameter('offset') or 0 try: self.imageDefaultSize = getSetting('image_default_size') except Exception as e: warn(self, 'Cannot set image default size, Error: {}', e)
def populate(self, Dir, lvl, whiteFlag, blackFlag): wanted = ['albums', 'tracks'] if self.search_type != 'all': wanted = [self.search_type] ret = False for kind in wanted: method = '_populate_%s' % kind if not hasattr(self, method): warn(self, "No method named %s" % method) continue if getattr(self, method)(Dir, lvl, whiteFlag, blackFlag): ret = True return ret
def populate(self, Dir, lvl, whiteFlag, blackFlag): data = api.get('/playlist/getUserPlaylists', username=self.name) if not data: warn(self, "No friend data") return False if lvl != -1: self.add_child(getNode(Flag.FRIENDS, self.parameters)) for pl in data['playlists']['items']: node = getNode(Flag.PLAYLIST) node.data = pl if node.get_owner() == self.label: self.nid = node.get_owner_id() self.add_child(node) return True
def create(self, line1, line2='', line3=''): """Create our progress dialog """ self.line1 = line1 self.line2 = line2 self.line3 = line3 if not self.is_enable: return True self.started_on = time.time() try: return super(Progress, self).create(line1, line2, line3) except: warn(self, "Cannot create progress bar") return False return True
def fetch(self, Dir, lvl, whiteFlag, blackFlag): if self.genre_type is None or self.genre_id is None: return True offset = self.offset or 0 limit = getSetting('pagination_limit') data = api.get('/album/getFeatured', type=RECOS_TYPE_IDS[int(self.genre_type)], genre_id=self.genre_id, limit=10, offset=offset) if data is None: warn(self, 'Cannot fetch data for recommendation') return False self.data = data return True
def __getFileUrl(self): hires = getSetting('hires_enabled', asBool=True) format_id = 6 if getSetting('streamtype') == 'flac' else 5 if hires and self.get_hires(): format_id = 27 if self.get_property('purchased') or self.get_parameter('purchased') == '1' or self.purchased: intent = "download" else: intent = "stream" data = api.get('/track/getFileUrl', format_id=format_id, track_id=self.nid, user_id=api.user_id, intent=intent) if not data: warn(self, "Cannot get stream type for track (network problem?)") return None return data
def populate(self, Dir, lvl, whiteFlag, blackFlag): if self.method is not None: return True ret = False all_kind = ('artists', 'albums', 'tracks') search_for = (self.search_type, ) if self.search_type is None: search_for = all_kind for kind in search_for: method = '_populate_%s' % kind if not hasattr(self, method): warn(self, 'No method named %s' % method) continue if getattr(self, method)(Dir, lvl, whiteFlag, blackFlag): ret = True return ret
def track_resportStreamingEnd(self, track_id, duration): duration = math.floor(int(duration)) if duration < 5: info(self, 'Duration lesser than 5s, abort reporting') return None # @todo ??? user_auth_token = '' # @UnusedVariable try: user_auth_token = self.user_auth_token # @UnusedVariable except: warn(self, 'No authentification token') return None params = { 'user_id': self.user_id, 'track_id': track_id, 'duration': duration } return self._api_request(params, '/track/reportStreamingEnd')
def _api_request(self, params, uri, **opt): '''Qobuz API HTTP get request Arguments: params: parameters dictionary uri : service/method opt : Optionnal named parameters - noToken=True/False Return None if something went wrong Return raw data from qobuz on success as dictionary * on error you can check error and status_code Example: ret = api._api_request({'username':'******', 'password':'******'}, 'user/login', noToken=True) print 'Error: %s [%s]' % (api.error, api.status_code) This should produce something like: Error: [200] Error: Bad Request [400] ''' self.statTotalRequest += 1 self.error = '' self.status_code = None url = self._baseUrl + uri useToken = False if (opt and 'noToken' in opt) else True headers = {} if useToken and self.user_auth_token: headers['x-user-auth-token'] = self.user_auth_token headers['x-app-id'] = self.appid '''DEBUG''' import copy _copy_params = copy.deepcopy(params) if 'password' in _copy_params: _copy_params['password'] = '******' info(self, 'URI {} POST PARAMS: {} HEADERS: {}', uri, str(_copy_params), str(headers)) '''END / DEBUG''' r = None try: r = self.session.post(url, data=params, headers=headers) except: self.error = 'Post request fail' warn(self, self.error) return None self.status_code = int(r.status_code) if self.status_code != 200: self.error = self._api_error_string(r, url, _copy_params) warn(self, self.error) return None if not r.content: self.error = 'Request return no content' warn(self, self.error) return None self.statContentSizeTotal += sys.getsizeof(r.content) '''Retry get if connexion fail''' try: response_json = r.json() except Exception as e: warn(self, 'Json loads failed to load... retrying!\n{}', repr(e)) try: response_json = r.json() except: self.error = "Failed to load json two times...abort" warn(self, self.error) return None status = None try: status = response_json['status'] except: pass if status == 'error': self.error = self._api_error_string(r, url, _copy_params, response_json) warn(self, self.error) return None return response_json