From 33bba9762c7f96a754351f3422f74364c5bdecac Mon Sep 17 00:00:00 2001 From: James Taylor Date: Sat, 10 Aug 2019 17:05:14 -0700 Subject: Convert subscription manager to flask framework --- youtube/subscriptions.py | 210 +++++++++++----------------- youtube/templates/subscription_manager.html | 141 +++++++++++++++++++ youtube/templates/unsubscribe_verify.html | 19 +++ 3 files changed, 242 insertions(+), 128 deletions(-) create mode 100644 youtube/templates/subscription_manager.html create mode 100644 youtube/templates/unsubscribe_verify.html (limited to 'youtube') diff --git a/youtube/subscriptions.py b/youtube/subscriptions.py index a238ae4..fa0570c 100644 --- a/youtube/subscriptions.py +++ b/youtube/subscriptions.py @@ -326,28 +326,38 @@ def check_specific_channels(channel_ids): +@yt_app.route('/import_subscriptions', methods=['POST']) +def import_subscriptions(): -def import_subscriptions(env, start_response): - content_type = env['parameters']['subscriptions_file'][0] - file = env['parameters']['subscriptions_file'][1] + # check if the post request has the file part + if 'subscriptions_file' not in request.files: + #flash('No file part') + return flask.redirect(util.URL_ORIGIN + request.full_path) + file = request.files['subscriptions_file'] + # if user does not select file, browser also + # submit an empty part without filename + if file.filename == '': + #flash('No selected file') + return flask.redirect(util.URL_ORIGIN + request.full_path) - file = file.decode('utf-8') - if content_type == 'application/json': + mime_type = file.mimetype + + if mime_type == 'application/json': + file = file.read().decode('utf-8') try: file = json.loads(file) except json.decoder.JSONDecodeError: traceback.print_exc() - start_response('400 Bad Request', () ) - return b'400 Bad Request: Invalid json file' + return '400 Bad Request: Invalid json file', 400 try: channels = ( (item['snippet']['resourceId']['channelId'], item['snippet']['title']) for item in file) except (KeyError, IndexError): traceback.print_exc() - start_response('400 Bad Request', () ) - return b'400 Bad Request: Unknown json structure' - elif content_type in ('application/xml', 'text/xml', 'text/x-opml'): + return '400 Bad Request: Unknown json structure', 400 + elif mime_type in ('application/xml', 'text/xml', 'text/x-opml'): + file = file.read().decode('utf-8') try: root = defusedxml.ElementTree.fromstring(file) assert root.tag == 'opml' @@ -363,57 +373,36 @@ def import_subscriptions(env, start_response): channels.append( (channel_id, channel_name) ) except (AssertionError, IndexError, defusedxml.ElementTree.ParseError) as e: - start_response('400 Bad Request', () ) - return b'400 Bad Request: Unable to read opml xml file, or the file is not the expected format' + return '400 Bad Request: Unable to read opml xml file, or the file is not the expected format', 400 else: - start_response('400 Bad Request', () ) - return b'400 Bad Request: Unsupported file format: ' + html.escape(content_type).encode('utf-8') + b'. Only subscription.json files (from Google Takeouts) and XML OPML files exported from Youtube\'s subscription manager page are supported' + return '400 Bad Request: Unsupported file format: ' + mime_type + '. Only subscription.json files (from Google Takeouts) and XML OPML files exported from Youtube\'s subscription manager page are supported', 400 with_open_db(_subscribe, channels) - start_response('303 See Other', [('Location', util.URL_ORIGIN + '/subscription_manager'),] ) - return b'' - + return flask.redirect(util.URL_ORIGIN + '/subscription_manager', 303) -"""sub_list_item_template = Template(''' -
  • - - $channel_name - $tags -
  • ''') -tag_group_template = Template(''' -
  • -

    $tag

    -
      -$sub_list -
    -
  • -''')""" -def get_subscription_manager_page(env, start_response): +@yt_app.route('/subscription_manager', methods=['GET']) +def get_subscription_manager_page(): + group_by_tags = request.args.get('group_by_tags', '0') == '1' with open_database() as connection: with connection as cursor: - if env['parameters'].get('group_by_tags', '0')[0] == '1': + if group_by_tags: + tag_groups = [] - sort_name = "Don't group" - sort_link = util.URL_ORIGIN + '/subscription_manager' - - main_list_html = '' - + sub_list.append({ + 'channel_url': util.URL_ORIGIN + '/channel/' + channel_id, + 'channel_name': channel_name, + 'channel_id': channel_id, + 'muted': muted, + 'tags': [], + }) + + tag_groups.append( ('No tags', sub_list) ) else: + sub_list = [] + for channel_name, channel_id, muted in _get_subscribed_channels(cursor): + sub_list.append({ + 'channel_url': util.URL_ORIGIN + '/channel/' + channel_id, + 'channel_name': channel_name, + 'channel_id': channel_id, + 'muted': muted, + 'tags': _get_tags(cursor, channel_id), + }) - sort_name = "Group by tags" - sort_link = util.URL_ORIGIN + '/subscription_manager?group_by_tags=1' - main_list_html = '
      ' - for channel_name, channel_id, muted in _get_subscribed_channels(cursor): - main_list_html += sub_list_item_template.substitute( - channel_url = util.URL_ORIGIN + '/channel/' + channel_id, - channel_name = html.escape(channel_name), - channel_id = channel_id, - tags = ', '.join(_get_tags(cursor, channel_id)), - mute_class = 'muted' if muted else '', - ) - main_list_html += '
    ' - - - - start_response('200 OK', [('Content-type','text/html'),]) - return subscription_manager_template.substitute( - header = html_common.get_header(), - main_list = main_list_html, - sort_name = sort_name, - sort_link = sort_link, - page_buttons = '', - ).encode('utf-8') + + + if group_by_tags: + return flask.render_template('subscription_manager.html', + group_by_tags = True, + tag_groups = tag_groups, + ) + else: + return flask.render_template('subscription_manager.html', + group_by_tags = False, + sub_list = sub_list, + ) def list_from_comma_separated_tags(string): return [tag.strip() for tag in string.split(',') if tag.strip()] -"""unsubscribe_list_item_template = Template(''' -
  • $channel_name
  • ''')""" -def post_subscription_manager_page(env, start_response): - params = env['parameters'] - action = params['action'][0] +@yt_app.route('/subscription_manager', methods=['POST']) +def post_subscription_manager_page(): + action = request.values['action'] with open_database() as connection: with connection as cursor: if action == 'add_tags': - _add_tags(cursor, params['channel_ids'], [tag.lower() for tag in list_from_comma_separated_tags(params['tags'][0])]) + _add_tags(cursor, request.values.getlist('channel_ids'), [tag.lower() for tag in list_from_comma_separated_tags(request.values['tags'])]) elif action == 'remove_tags': - _remove_tags(cursor, params['channel_ids'], [tag.lower() for tag in list_from_comma_separated_tags(params['tags'][0])]) + _remove_tags(cursor, request.values.getlist('channel_ids'), [tag.lower() for tag in list_from_comma_separated_tags(request.values['tags'])]) elif action == 'unsubscribe': - _unsubscribe(cursor, params['channel_ids']) + _unsubscribe(cursor, request.values.getlist('channel_ids')) elif action == 'unsubscribe_verify': - page = ''' - Are you sure you want to unsubscribe from these channels? -
    ''' - - for channel_id in params['channel_ids']: - page += '\n' - - page += ''' - - -
    - ''' - - start_response('200 OK', [('Content-type','text/html'),]) - return html_common.yt_basic_template.substitute( - page_title = 'Unsubscribe?', - style = '', - header = html_common.get_header(), - page = page, - ).encode('utf-8') + unsubscribe_list = _get_channel_names(cursor, request.values.getlist('channel_ids')) + return flask.render_template('unsubscribe_verify.html', unsubscribe_list = unsubscribe_list) + elif action == 'mute': cursor.executemany('''UPDATE subscribed_channels SET muted = 1 - WHERE yt_channel_id = ?''', [(ci,) for ci in params['channel_ids']]) + WHERE yt_channel_id = ?''', [(ci,) for ci in request.values.getlist('channel_ids')]) elif action == 'unmute': cursor.executemany('''UPDATE subscribed_channels SET muted = 0 - WHERE yt_channel_id = ?''', [(ci,) for ci in params['channel_ids']]) - + WHERE yt_channel_id = ?''', [(ci,) for ci in request.values.getlist('channel_ids')]) else: - start_response('400 Bad Request', ()) - return b'400 Bad Request' - - start_response('303 See Other', [('Location', util.URL_ORIGIN + '/subscription_manager'),] ) - return b'' + flask.abort(400) + return flask.redirect(util.URL_ORIGIN + request.full_path, 303) @yt_app.route('/subscriptions', methods=['GET']) @yt_app.route('/feed/subscriptions', methods=['GET']) diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html new file mode 100644 index 0000000..838f4af --- /dev/null +++ b/youtube/templates/subscription_manager.html @@ -0,0 +1,141 @@ +{% set page_title = 'Subscription Manager' %} +{% extends "base.html" %} +{% block style %} + .import-export{ + display: flex; + flex-direction: row; + } + .subscriptions-import-form{ + background-color: #dadada; + display: flex; + flex-direction: column; + align-items: flex-start; + max-width: 300px; + padding:10px; + } + .subscriptions-import-form h2{ + font-size: 20px; + margin-bottom: 10px; + } + + .import-submit-button{ + margin-top:15px; + align-self: flex-end; + } + + + .subscriptions-export-links{ + margin: 0px 0px 0px 20px; + background-color: #dadada; + list-style: none; + max-width: 300px; + padding:10px; + } + + .sub-list-controls{ + background-color: #dadada; + padding:10px; + } + + + .tag-group-list{ + list-style: none; + margin-left: 10px; + margin-right: 10px; + padding: 0px; + } + .tag-group{ + border-style: solid; + margin-bottom: 10px; + } + + .sub-list{ + list-style: none; + padding:10px; + column-width: 300px; + column-gap: 40px; + } + .sub-list-item{ + display:flex; + margin-bottom: 10px; + background-color: #dadada; + } + .tag-list{ + margin-left:15px; + font-weight:bold; + } + .sub-list-item-name{ + margin-left:15px; + } + .sub-list-checkbox{ + height: 1.5em; + width: 1.5em; + } + .muted{ + background-color: #888888; + } +{% endblock style %} + + +{% macro subscription_list(sub_list) %} + {% for subscription in sub_list %} +
  • + + {{ subscription['channel_name'] }} + {{ ', '.join(subscription['tags']) }} +
  • + {% endfor %} +{% endmacro %} + + + +{% block main %} +
    +
    +

    Import subscriptions

    + + +
    + + +
    + +
    + +
    + {% if group_by_tags %} + Don't group + {% else %} + Group by tags + {% endif %} + + + + + + + +
    + + + {% if group_by_tags %} + + {% else %} +
      + {{ subscription_list(sub_list) }} +
    + {% endif %} + +{% endblock main %} diff --git a/youtube/templates/unsubscribe_verify.html b/youtube/templates/unsubscribe_verify.html new file mode 100644 index 0000000..98581c0 --- /dev/null +++ b/youtube/templates/unsubscribe_verify.html @@ -0,0 +1,19 @@ +{% set page_title = 'Unsubscribe?' %} +{% extends "base.html" %} + +{% block main %} + Are you sure you want to unsubscribe from these channels? +
    + {% for channel_id, channel_name in unsubscribe_list %} + + {% endfor %} + + + +
    + +{% endblock main %} -- cgit v1.2.3