def __init__(self): super(VideoListPopular, self).__init__() self.get_style_context().add_class('video_list_popular') entries = search_youtube_by_keyword(popular=True, max_results=3, parent_control=self.ParentalControl, start_index=randint(1, 20)) if entries: parsed_entries = parse_youtube_entries(entries) x_pos = 0 for i, e in enumerate(parsed_entries): img = Gtk.Image() button = Button() if e['big_thumb']: thumbnail = '{}/video_{}.jpg'.format(tmp_dir, time()) download_url(e['big_thumb'], thumbnail) img.set_from_file(thumbnail) button.add(img) button.connect('clicked', self._play, e['video_url']) self._grid.attach(button, x_pos, 0, 1, 1) x_pos += 1
def download_online_badges(self): profile = load_profile() if "kanoworld_id" in profile: user_id = profile["kanoworld_id"] else: return False, "Profile not registered!" success, text, data = request_wrapper("get", "/users/{}".format(user_id), session=self.session) if not success: return False, text if "user" not in data: return False, "Corrupt response (the 'user' key not found)" if "profile" not in data["user"]: return False, "Corrupt response (the 'user.profile' key not found)" if "badges" not in data["user"]["profile"]: msg = "Corrupt response (the 'user.profile.badges' key not found)" return False, msg online_badges_data = {} ensure_dir(online_badges_dir) badges = data["user"]["profile"]["badges"] for badge in badges: if "assigned" not in badge or not badge["assigned"]: continue if "image_url" not in badge: return False, "Couldn't find an image for the badge" image_loc = os.path.join(online_badges_dir, "{}.png".format(badge["id"])) download_url(badge["image_url"], image_loc) online_badges_data[badge["id"]] = { "achieved": True, "bg_color": badge["bg_color"].replace("#", ""), "desc_locked": badge["desc_locked"], "desc_unlocked": badge["desc_unlocked"], "title": badge["title"], } try: may_write = True txt = None f = open(online_badges_file, "w") except IOError as e: may_write = False txt = "Error opening badges file {}".format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if "SUDO_USER" in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
def __init__(self): super(VideoListPopular, self).__init__() self.get_style_context().add_class('video_list_popular') entries = search_youtube_by_keyword( popular=True, max_results=3, parent_control=self.ParentalControl, start_index=randint(1, 20)) if entries: parsed_entries = parse_youtube_entries(entries) x_pos = 0 for i, e in enumerate(parsed_entries): img = Gtk.Image() button = Button() if e['big_thumb']: thumbnail = '{}/video_{}.jpg'.format(tmp_dir, time()) download_url(e['big_thumb'], thumbnail) img.set_from_file(thumbnail) button.add(img) button.connect('clicked', self._play, e['video_url']) self._grid.attach(button, x_pos, 0, 1, 1) x_pos += 1
def download_app(app_id_or_slug): data = query_for_app(app_id_or_slug) # download the icon icon_file_type = data['icon_url'].split(".")[-1] icon_path = '/tmp/{}.{}'.format(app_id_or_slug, icon_file_type) rv, err = download_url(data['icon_url'], icon_path) if not rv: msg = "Unable to download the application ({})".format(err) raise AppDownloadError(msg) # Check if the app isn't rpi2 only if not is_model_2_b() and 'rpi2_only' in data and data['rpi2_only']: msg = "{} won't be downloaded ".format(data['title']) + \ "becuase it's Raspberry Pi 2 only" raise AppDownloadError(msg) # Cleanup the JSON file data['icon'] = data['slug'] del data['icon_url'] del data['likes'] del data['comments_count'] data['time_installed'] = int(time.time()) data['categories'] = map(lambda c: c.lower(), data['categories']) data['removable'] = True # write out the data data_path = '/tmp/{}.app'.format(app_id_or_slug) with open(data_path, 'w') as f: f.write(json.dumps(data)) return [data_path, icon_path]
def restore_content(self, file_path): success, text, data = request_wrapper("get", "/sync/backup", session=self.session) if not success: return False, text try: file_url = data["user_backup"]["file_url"] except Exception: file_url = None if not file_url: return False, "backup file not found" return download_url(file_url, file_path)
def restore_content(self, file_path): success, text, data = request_wrapper('get', '/sync/backup', session=self.session) if not success: return False, text try: file_url = data['user_backup']['file_url'] except Exception: file_url = None if not file_url: return False, 'backup file not found' return download_url(file_url, file_path)
def restore_content(self, file_path): success, text, data = request_wrapper('get', '/sync/backup', session=self.session) if not success: return False, text try: file_url = data['user_backup']['file_url'] except Exception: file_url = None if not file_url: return False, "backup file not found" return download_url(file_url, file_path)
def download_app(app_id_or_slug): data = query_for_app(app_id_or_slug) # download the icon icon_file_type = data['icon_url'].split(".")[-1] icon_path = '/tmp/{}.{}'.format(app_id_or_slug, icon_file_type) rv, err = download_url(data['icon_url'], icon_path) if not rv: msg = _("Unable to download the application ({})").format(err) raise AppDownloadError(msg) # Check if the app is going to run well on the hardware if 'min_performance_score' in data and \ has_min_performance(data['min_performance_score']): msg = _( "{} won't be downloaded " "because the hardware is not performant enough" ).format(data['title']) raise AppDownloadError(msg) # Cleanup the JSON file data['icon'] = data['slug'] del data['icon_url'] del data['likes'] del data['comments_count'] data['time_installed'] = int(time.time()) data['categories'] = map(lambda c: c.lower(), data['categories']) data['removable'] = True # FIXME: This should all be done in the API if 'priority' not in data: data['priority'] = get_prio(data['slug']) if data['slug'] == 'powerup': data['title'] = 'Make Light' # write out the data data_path = '/tmp/{}.app'.format(app_id_or_slug) with open(data_path, 'w') as f: f.write(json.dumps(data)) return [data_path, icon_path]
def _process_notification(self, entry): """ Cherry picks information from a Kano World notification based on its type and returns it in a dict. :param entry: A notification entry from the World API :returns: A dict that can be passed to the notification widget """ notification_dir = os.path.join(media_dir, 'images', 'notification', '280x170') FEATURED_SHARE_IMG = os.path.join(notification_dir, 'featured.png') PONG_SHARE_IMG = os.path.join(notification_dir, 'share-pong.png') MUSIC_SHARE_IMG = os.path.join(notification_dir, 'share-music.png') LIGHT_SHARE_IMG = os.path.join(notification_dir, 'share-light.png') ART_SHARE_IMG = os.path.join(notification_dir, 'share-art.png') SNAKE_SHARE_IMG = os.path.join(notification_dir, 'snake-art.png') MINECRAFT_SHARE_IMG = os.path.join(notification_dir, 'share-minecraft.png') FOLLOWER_IMG = os.path.join(notification_dir, 'follower.png') COMMENT_IMG = os.path.join(notification_dir, 'comment.png') LIKE_IMG = os.path.join(notification_dir, 'like.png') GENERIC_ALERT_IMG = os.path.join(notification_dir, 'notification.png') n = { 'id': entry['id'], 'title': 'Kano World', 'byline': '', 'image': GENERIC_ALERT_IMG, 'command': 'kano-world-launcher /notifications/open/{}'.format(entry['id']) } # Some notifications may have images # If so, we need to download them and resize if 'image_url' in entry and entry['image_url']: filename = os.path.basename(entry['image_url']) img_path = "{}/notifications/{}".format(profile_dir, filename) ensure_dir(os.path.dirname(img_path)) rv, e = download_url(entry['image_url'], img_path) if rv: # Resize image to 280x170 # FIXME: We import GdkPixbuf locally to make sure not to # bugger up anything else, but we should move it up to the top. from gi.repository import GdkPixbuf pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( img_path, 280, 170) pixbuf.savev(img_path, 'png', [None], [None]) n['image'] = img_path else: msg = "Notifications image failed to download ({}).".format(e) logger.error(msg) # Certain notifications may come with a command as well. # If so, override the default one. cmd = self._get_dict_value(entry, ['meta', 'cmd']) if cmd: n['command'] = cmd # Customise settings for known types if entry['category'] == 'follows': n['title'] = 'New follower!' n['byline'] = entry['title'] n['image'] = FOLLOWER_IMG elif entry['category'] in ['share-items', 'shares']: n['title'] = 'New share!' n['byline'] = entry['title'] if entry['type'] == 'make-minecraft': n['image'] = MINECRAFT_SHARE_IMG elif entry['type'] == 'make-pong': n['image'] = PONG_SHARE_IMG elif entry['type'] == 'make-music': n['image'] = MUSIC_SHARE_IMG elif entry['type'] == 'make-light': n['image'] = LIGHT_SHARE_IMG elif entry['type'] == 'kano-draw': n['image'] = ART_SHARE_IMG elif entry['type'] == 'featured': n['title'] = 'Staff picked!' n['image'] = FEATURED_SHARE_IMG elif entry['category'] == 'comments': n['title'] = 'New comment!' n['byline'] = entry['title'] n['image'] = COMMENT_IMG elif entry['category'] == 'likes': n['title'] = 'New like!' n['byline'] = entry['title'] n['image'] = LIKE_IMG else: return None return n
def download_online_badges(self): profile = load_profile() if 'kanoworld_id' in profile: user_id = profile['kanoworld_id'] else: return False, 'Profile not registered!' success, text, data = request_wrapper('get', '/users/{}'.format(user_id), session=self.session) if not success: return False, text if 'user' not in data: return False, _("Corrupt response (the 'user' key not found)") if 'profile' not in data['user']: return False, _( "Corrupt response (the 'user.profile' key not found)") if 'badges' not in data['user']['profile']: return False, _( "Corrupt response (the 'user.profile.badges' key not found)") online_badges_data = {} ensure_dir(online_badges_dir) badges = data['user']['profile']['badges'] for badge in badges: if 'assigned' not in badge or not badge['assigned']: continue if 'image_url' not in badge: return False, _("Couldn't find an image for the badge") image_loc = os.path.join(online_badges_dir, "{}.png".format(badge['id'])) download_url(badge['image_url'], image_loc) online_badges_data[badge['id']] = { 'achieved': True, 'bg_color': badge['bg_color'].replace("#", ""), 'desc_locked': badge['desc_locked'], 'desc_unlocked': badge['desc_unlocked'], 'title': badge['title'] } try: may_write = True txt = None f = open(online_badges_file, 'w') except IOError as e: may_write = False txt = 'Error opening badges file {}'.format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if 'SUDO_USER' in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
def _process_notification(self, entry): """ Cherry picks information from a Kano World notification based on its type and returns it in a dict. :param entry: A notification entry from the World API :returns: A dict that can be passed to the notification widget """ notification_dir = os.path.join( media_dir, 'images', 'notification', '280x170' ) FEATURED_SHARE_IMG = os.path.join(notification_dir, 'featured.png') PONG_SHARE_IMG = os.path.join(notification_dir, 'share-pong.png') MUSIC_SHARE_IMG = os.path.join(notification_dir, 'share-music.png') LIGHT_SHARE_IMG = os.path.join(notification_dir, 'share-light.png') ART_SHARE_IMG = os.path.join(notification_dir, 'share-art.png') SNAKE_SHARE_IMG = os.path.join(notification_dir, 'snake-art.png') MINECRAFT_SHARE_IMG = os.path.join(notification_dir, 'share-minecraft.png') FOLLOWER_IMG = os.path.join(notification_dir, 'follower.png') COMMENT_IMG = os.path.join(notification_dir, 'comment.png') LIKE_IMG = os.path.join(notification_dir, 'like.png') GENERIC_ALERT_IMG = os.path.join(notification_dir, 'notification.png') n = { 'id': entry['id'], 'title': 'Kano World', 'byline': '', 'image': GENERIC_ALERT_IMG, 'command': 'kano-world-launcher /notifications/open/{}'.format( entry['id']) } # Some notifications may have images # If so, we need to download them and resize if 'image_url' in entry and entry['image_url']: filename = os.path.basename(entry['image_url']) img_path = "{}/notifications/{}".format(profile_dir, filename) ensure_dir(os.path.dirname(img_path)) rv, e = download_url(entry['image_url'], img_path) if rv: # Resize image to 280x170 # FIXME: We import GdkPixbuf locally to make sure not to # bugger up anything else, but we should move it up to the top. from gi.repository import GdkPixbuf pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( img_path, 280, 170) pixbuf.savev(img_path, 'png', [None], [None]) n['image'] = img_path else: msg = "Notifications image failed to download ({}).".format(e) logger.error(msg) # Certain notifications may come with a command as well. # If so, override the default one. cmd = self._get_dict_value(entry, ['meta', 'cmd']) if cmd: n['command'] = cmd # Customise settings for known types if entry['category'] == 'follows': n['title'] = 'New follower!' n['byline'] = entry['title'] n['image'] = FOLLOWER_IMG elif entry['category'] in ['share-items', 'shares']: n['title'] = 'New share!' n['byline'] = entry['title'] if entry['type'] == 'make-minecraft': n['image'] = MINECRAFT_SHARE_IMG elif entry['type'] == 'make-pong': n['image'] = PONG_SHARE_IMG elif entry['type'] == 'make-music': n['image'] = MUSIC_SHARE_IMG elif entry['type'] == 'make-light': n['image'] = LIGHT_SHARE_IMG elif entry['type'] == 'kano-draw': n['image'] = ART_SHARE_IMG elif entry['type'] == 'featured': n['title'] = 'Staff picked!' n['image'] = FEATURED_SHARE_IMG elif entry['category'] == 'comments': n['title'] = 'New comment!' n['byline'] = entry['title'] n['image'] = COMMENT_IMG elif entry['category'] == 'likes': n['title'] = 'New like!' n['byline'] = entry['title'] n['image'] = LIKE_IMG else: return None return n
def download_online_badges(self): profile = load_profile() if 'kanoworld_id' in profile: user_id = profile['kanoworld_id'] else: return False, 'Profile not registered!' success, text, data = request_wrapper( 'get', '/users/{}'.format(user_id), session=self.session ) if not success: return False, text if 'user' not in data: return False, _("Corrupt response (the 'user' key not found)") if 'profile' not in data['user']: return False, _("Corrupt response (the 'user.profile' key not found)") if 'badges' not in data['user']['profile']: return False, _("Corrupt response (the 'user.profile.badges' key not found)") online_badges_data = {} ensure_dir(online_badges_dir) badges = data['user']['profile']['badges'] for badge in badges: if 'assigned' not in badge or not badge['assigned']: continue if 'image_url' not in badge: return False, _("Couldn't find an image for the badge") image_loc = os.path.join(online_badges_dir, "{}.png".format(badge['id'])) download_url(badge['image_url'], image_loc) online_badges_data[badge['id']] = { 'achieved': True, 'bg_color': badge['bg_color'].replace("#", ""), 'desc_locked': badge['desc_locked'], 'desc_unlocked': badge['desc_unlocked'], 'title': badge['title'] } try: may_write = True txt = None f = open(online_badges_file, 'w') except IOError as e: may_write = False txt = 'Error opening badges file {}'.format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if 'SUDO_USER' in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
def download_share(entry): app = entry['app'] title = entry['title'] description = entry['description'] attachment_url = entry['attachment_url'] cover_url = entry['cover_url'] resource_url = entry['resource_url'] data = { 'title': title, 'description': description } app_profiles = read_json(app_profiles_file) if app not in app_profiles: logger.error("Cannot download share, app not found in app-profiles") return app_profile = app_profiles[app] folder = os.path.join(get_home(), app_profile['dir'], 'webload') ensure_dir(folder) title_slugified = slugify(title) # Download attachment attachment_ext = attachment_url.split('.')[-1] attachment_name = '{}.{}'.format(title_slugified, attachment_ext) attachment_path = os.path.join(folder, attachment_name) success, text = download_url(attachment_url, attachment_path) if not success: msg = "Error with downloading share file: {}".format(text) logger.error(msg) return False, msg # Download screenshot if cover_url: cover_ext = cover_url.split('.')[-1] cover_name = '{}.{}'.format(title_slugified, cover_ext) cover_path = os.path.join(folder, cover_name) success, text = download_url(cover_url, cover_path) if not success: msg = "Error with downloading cover file: {}".format(text) logger.error(msg) return False, msg # Download resource file if resource_url: resource_ext = resource_url.split('.')[-1] # Make sure we don't remove the tar from gz if 'tar.gz' in resource_url: resource_ext = 'tar.' + resource_ext resource_name = '{}.{}'.format(title_slugified, resource_ext) resource_path = os.path.join(folder, resource_name) success, text = download_url(resource_url, resource_path) if not success: msg = "Error with downloading resource file: {}".format(text) logger.error(msg) return False, msg # JSON file json_name = '{}.{}'.format(title_slugified, 'json') json_path = os.path.join(folder, json_name) write_json(json_path, data) return True, [title, attachment_path, app, attachment_name, folder]
def __init__(self, e, playlist_name=None, permanent=False): super(VideoEntry, self).__init__(hexpand=True) self._playlist_name = playlist_name self._permanent = permanent self.get_style_context().add_class('entry_item') self.connect('clicked', self._detail_view_handler, e) button_grid = Gtk.Grid() button_grid.set_column_spacing(30) self.add(button_grid) img = Gtk.Image() if e['thumbnail']: thumbnail = '{}/video_{}.jpg'.format(tmp_dir, time()) download_url(e['thumbnail'], thumbnail) img.set_from_file(thumbnail) else: img.set_from_file('{}/icons/no_thumbnail.png'.format(image_dir)) img.set_size_request(self._ENTRY_HEIGHT, self._ENTRY_HEIGHT) img.get_style_context().add_class('thumb') button_grid.attach(img, 0, 0, 1, 4) title_str = e['title'] if len( e['title']) <= 70 else e['title'][:67] + '...' label = Gtk.Label(title_str, hexpand=True) label.set_alignment(0, 0.5) label.get_style_context().add_class('title') button_grid.attach(label, 1, 0, 1, 1) if playlist_name and not permanent: remove = RemoveButton() remove.connect('clicked', self._remove_from_playlist_handler, e, playlist_name) button_grid.attach(remove, 2, 0, 1, 1) if e['local_path'] is None: stats_str = '{}K views - {}:{} min - by {}'.format( int(e['viewcount'] / 1000.0), e['duration_min'], e['duration_sec'], e['author']) label = Gtk.Label(stats_str) label.get_style_context().add_class('subtitle') label.set_alignment(0, 0.5) button_grid.attach(label, 1, 1, 2, 1) desc_str = e['description'] if len( e['description']) <= 100 else e['description'][:97] + '...' label = Gtk.Label(desc_str) label.get_style_context().add_class('subtitle') label.set_alignment(0, 0.5) button_grid.attach(label, 1, 2, 2, 1) action_grid = Gtk.Grid() button_grid.attach(action_grid, 1, 3, 2, 1) button = Button('WATCH') button.get_style_context().add_class('orange_linktext') self._button_handler_id = button.connect('clicked', self._play_handler, e['video_url'], e['local_path']) action_grid.attach(button, 0, 0, 1, 1) if not playlist_name: action_grid.attach(Spacer(), 1, 0, 1, 1) button = Button('SAVE') button.get_style_context().add_class('orange_linktext') self._button_handler_id = button.connect( 'clicked', self.add_to_playlist_handler, e) action_grid.attach(button, 2, 0, 1, 1)
def _process_notification(self, entry): """ Cherry picks information from a Kano World notification based on its type and returns it in a dict. :param entry: A notification entry from the World API :returns: A dict that can be passed to the notification widget """ MINECRAFT_SHARE_IMG = media_dir + "/images/notification/280x170/share-pong.png" PONG_SHARE_IMG = media_dir + "/images/notification/280x170/share-minecraft.png" SP_IMG = media_dir + "/images/notification/280x170/saturday-project.png" FOLLOWER_IMG = media_dir + "/images/notification/280x170/follower.png" GENERIC_ALERT_IMG = media_dir + "/images/notification/280x170/notification.png" n = { "id": entry["id"], "title": "Kano World", "byline": "", "image": GENERIC_ALERT_IMG, "command": "kano-world-launcher /notifications/open/{}".format(entry["id"]), } # Customise settings for known types if entry["category"] == "follows": n["title"] = "New follower!" n["byline"] = entry["title"] n["image"] = FOLLOWER_IMG # Link to whomever followed this user user = self._get_dict_value(entry, ["meta", "author", "username"]) if user: n["command"] = "kano-world-launcher /users/{}".format(user) elif entry["category"] in ["share-items", "shares"]: n["title"] = "New share!" n["byline"] = entry["title"] if entry["type"] == "make-minecraft": n["image"] = MINECRAFT_SHARE_IMG elif entry["type"] == "make-pong": n["image"] = PONG_SHARE_IMG # Link to the share share_id = self._get_dict_value(entry, ["meta", "item", "id"]) if share_id: n["command"] = "kano-world-launcher /shared/{}".format(share_id) elif entry["category"] == "comments": n["title"] = "New comment!" n["byline"] = entry["title"] slug = self._get_dict_value(entry, ["meta", "item", "slug"]) if slug: obj_type = entry["meta"]["item"]["type"] if obj_type == "app": n["command"] = "kano-world-launcher /apps/{}".format(slug) elif obj_type == "share": n["command"] = "kano-world-launcher /shared/{}".format(slug) elif obj_type == "project": n["command"] = "kano-world-launcher /projects/{}".format(slug) # If a notification has both the title and text, override the default if "title" in entry and entry["title"] and "text" in entry and entry["text"]: n["title"] = entry["title"] n["byline"] = entry["text"] # Some notifications may have images # If so, we need to download them and resize if "image_url" in entry and entry["image_url"]: filename = os.path.basename(entry["image_url"]) img_path = "{}/notifications/{}".format(profile_dir, filename) ensure_dir(os.path.dirname(img_path)) rv, e = download_url(entry["image_url"], img_path) if rv: # Resize image to 280x170 # FIXME: We import GdkPixbuf locally to make sure not to # bugger up anything else, but we should move it up to the top. from gi.repository import GdkPixbuf pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(img_path, 280, 170) pixbuf.savev(img_path, "png", [None], [None]) n["image"] = img_path else: msg = "Notifications image failed to download ({}).".format(e) logger.error(msg) # Certain notifications may come with a command as well. # If so, override the default one. cmd = self._get_dict_value(entry, ["meta", "cmd"]) if cmd: n["command"] = cmd return n
def __init__(self, e, playlist_name=None, permanent=False): super(VideoEntry, self).__init__(hexpand=True) self._playlist_name = playlist_name self._permanent = permanent self.get_style_context().add_class('entry_item') self.connect('clicked', self._detail_view_handler, e) button_grid = Gtk.Grid() button_grid.set_column_spacing(30) self.add(button_grid) img = Gtk.Image() if e['thumbnail']: thumbnail = '{}/video_{}.jpg'.format(tmp_dir, time()) download_url(e['thumbnail'], thumbnail) img.set_from_file(thumbnail) else: img.set_from_file('{}/icons/no_thumbnail.png'.format(image_dir)) img.set_size_request(self._ENTRY_HEIGHT, self._ENTRY_HEIGHT) img.get_style_context().add_class('thumb') button_grid.attach(img, 0, 0, 1, 4) title_str = e['title'] if len(e['title']) <= 70 else e['title'][:67] + '...' label = Gtk.Label(title_str, hexpand=True) label.set_alignment(0, 0.5) label.get_style_context().add_class('title') button_grid.attach(label, 1, 0, 1, 1) if playlist_name and not permanent: remove = RemoveButton() remove.connect('clicked', self._remove_from_playlist_handler, e, playlist_name) button_grid.attach(remove, 2, 0, 1, 1) if e['local_path'] is None: stats_str = '{}K views - {}:{} min - by {}'.format(int(e['viewcount'] / 1000.0), e['duration_min'], e['duration_sec'], e['author']) label = Gtk.Label(stats_str) label.get_style_context().add_class('subtitle') label.set_alignment(0, 0.5) button_grid.attach(label, 1, 1, 2, 1) desc_str = e['description'] if len(e['description']) <= 100 else e['description'][:97] + '...' label = Gtk.Label(desc_str) label.get_style_context().add_class('subtitle') label.set_alignment(0, 0.5) button_grid.attach(label, 1, 2, 2, 1) action_grid = Gtk.Grid() button_grid.attach(action_grid, 1, 3, 2, 1) button = Button('WATCH') button.get_style_context().add_class('orange_linktext') self._button_handler_id = button.connect('clicked', self._play_handler, e['video_url'], e['local_path']) action_grid.attach(button, 0, 0, 1, 1) if not playlist_name: action_grid.attach(Spacer(), 1, 0, 1, 1) button = Button('SAVE') button.get_style_context().add_class('orange_linktext') self._button_handler_id = button.connect('clicked', self.add_to_playlist_handler, e) action_grid.attach(button, 2, 0, 1, 1)
def download_share(entry): app = entry['app'] title = entry['title'] description = entry['description'] attachment_url = entry['attachment_url'] cover_url = entry['cover_url'] resource_url = entry['resource_url'] data = {'title': title, 'description': description} app_profiles = read_json(app_profiles_file) if app not in app_profiles: logger.error("Cannot download share, app not found in app-profiles") return app_profile = app_profiles[app] folder = os.path.join(get_home(), app_profile['dir'], 'webload') ensure_dir(folder) title_slugified = slugify(title) # Download attachment attachment_ext = attachment_url.split('.')[-1] attachment_name = '{}.{}'.format(title_slugified, attachment_ext) attachment_path = os.path.join(folder, attachment_name) success, text = download_url(attachment_url, attachment_path) if not success: msg = "Error with downloading share file: {}".format(text) logger.error(msg) return False, msg # Download screenshot if cover_url: cover_ext = cover_url.split('.')[-1] cover_name = '{}.{}'.format(title_slugified, cover_ext) cover_path = os.path.join(folder, cover_name) success, text = download_url(cover_url, cover_path) if not success: msg = "Error with downloading cover file: {}".format(text) logger.error(msg) return False, msg # Download resource file if resource_url: resource_ext = resource_url.split('.')[-1] # Make sure we don't remove the tar from gz if 'tar.gz' in resource_url: resource_ext = 'tar.' + resource_ext resource_name = '{}.{}'.format(title_slugified, resource_ext) resource_path = os.path.join(folder, resource_name) success, text = download_url(resource_url, resource_path) if not success: msg = "Error with downloading resource file: {}".format(text) logger.error(msg) return False, msg # JSON file json_name = '{}.{}'.format(title_slugified, 'json') json_path = os.path.join(folder, json_name) write_json(json_path, data) return True, [title, attachment_path, app, attachment_name, folder]
def _process_notification(self, entry): """ Cherry picks information from a Kano World notification based on its type and returns it in a dict. :param entry: A notification entry from the World API :returns: A dict that can be passed to the notification widget """ MINECRAFT_SHARE_IMG = media_dir + \ '/images/notification/280x170/share-pong.png' PONG_SHARE_IMG = media_dir + \ '/images/notification/280x170/share-minecraft.png' SP_IMG = media_dir + \ '/images/notification/280x170/saturday-project.png' FOLLOWER_IMG = media_dir + \ '/images/notification/280x170/follower.png' GENERIC_ALERT_IMG = media_dir + \ '/images/notification/280x170/notification.png' n = { 'id': entry['id'], 'title': 'Kano World', 'byline': '', 'image': GENERIC_ALERT_IMG, 'command': 'kano-world-launcher /notifications/open/{}'.format( entry['id']) } # Customise settings for known types if entry['category'] == 'follows': n['title'] = 'New follower!' n['byline'] = entry['title'] n['image'] = FOLLOWER_IMG # Link to whomever followed this user user = self._get_dict_value(entry, ['meta', 'author', 'username']) if user: n['command'] = "kano-world-launcher /users/{}".format(user) elif entry['category'] in ['share-items', 'shares']: n['title'] = 'New share!' n['byline'] = entry['title'] if entry['type'] == 'make-minecraft': n['image'] = MINECRAFT_SHARE_IMG elif entry['type'] == 'make-pong': n['image'] = PONG_SHARE_IMG # Link to the share share_id = self._get_dict_value(entry, ['meta', 'item', 'id']) if share_id: n['command'] = "kano-world-launcher /shared/{}".format( share_id) elif entry['category'] == 'comments': n['title'] = 'New comment!' n['byline'] = entry['title'] slug = self._get_dict_value(entry, ['meta', 'item', 'slug']) if slug: obj_type = entry['meta']['item']['type'] if obj_type == "app": n['command'] = "kano-world-launcher /apps/{}".format(slug) elif obj_type == "share": n['command'] = "kano-world-launcher /shared/{}".format( slug) elif obj_type == "project": n['command'] = "kano-world-launcher /projects/{}".format( slug) # If a notification has both the title and text, override the default if 'title' in entry and entry['title'] and \ 'text' in entry and entry['text']: n['title'] = entry['title'] n['byline'] = entry['text'] # Some notifications may have images # If so, we need to download them and resize if 'image_url' in entry and entry['image_url']: filename = os.path.basename(entry['image_url']) img_path = "{}/notifications/{}".format(profile_dir, filename) ensure_dir(os.path.dirname(img_path)) rv, e = download_url(entry['image_url'], img_path) if rv: # Resize image to 280x170 # FIXME: We import GdkPixbuf locally to make sure not to # bugger up anything else, but we should move it up to the top. from gi.repository import GdkPixbuf pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( img_path, 280, 170) pixbuf.savev(img_path, 'png', [None], [None]) n['image'] = img_path else: msg = "Notifications image failed to download ({}).".format(e) logger.error(msg) # Certain notifications may come with a command as well. # If so, override the default one. cmd = self._get_dict_value(entry, ['meta', 'cmd']) if cmd: n['command'] = cmd return n