aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--youtube/static/subscription_manager.css39
-rw-r--r--youtube/subscriptions.py44
-rw-r--r--youtube/templates/subscription_manager.html22
-rw-r--r--youtube/templates/subscriptions.xml9
4 files changed, 107 insertions, 7 deletions
diff --git a/youtube/static/subscription_manager.css b/youtube/static/subscription_manager.css
index 1e260d8..7ddd90e 100644
--- a/youtube/static/subscription_manager.css
+++ b/youtube/static/subscription_manager.css
@@ -227,7 +227,8 @@ hr {
grid-template-columns: 1fr;
margin: auto;
grid-template-areas:
- "subscriptions-import-form";
+ "subscriptions-import-form"
+ "subscriptions-export-form";
align-items: center;
justify-items: center;
justify-content: center;
@@ -237,12 +238,30 @@ hr {
text-align: center;
}
+.subscriptions-import-options {
+ display: grid;
+ grid-template-columns: repeat(1, auto);
+ margin: auto;
+}
+
+.subscriptions-export-form {
+ grid-area: subscriptions-export-form;
+ text-align: center;
+}
+
+.subscriptions-export-options {
+ display: grid;
+ grid-template-columns: repeat(1, auto);
+ margin: auto;
+}
+
.sub-list-controls {
display: grid;
grid-row-gap: 0.2rem;
}
.subscriptions-import-form input[type='submit'],
+.subscriptions-export-form input[type='submit'],
.sub-list-controls button[type='submit'],
.sub-list-controls input[type='reset'] {
cursor: pointer;
@@ -323,6 +342,24 @@ hr {
.title {
font-size: 1rem;
}
+ .subscriptions-import-options {
+ display: grid;
+ grid-template-columns: repeat(2, auto);
+ margin: auto;
+ align-items: center;
+ justify-items: center;
+ justify-content: center;
+ grid-column-gap: 0.5rem;
+ }
+ .subscriptions-export-options {
+ display: grid;
+ grid-template-columns: repeat(4, auto);
+ margin: auto;
+ align-items: center;
+ justify-items: center;
+ justify-content: center;
+ grid-column-gap: 0.5rem;
+ }
}
@media (min-width: 600px) {
diff --git a/youtube/subscriptions.py b/youtube/subscriptions.py
index c18f822..f540e35 100644
--- a/youtube/subscriptions.py
+++ b/youtube/subscriptions.py
@@ -732,6 +732,50 @@ def import_subscriptions():
return flask.redirect(util.URL_ORIGIN + '/subscription_manager', 303)
+@yt_app.route('/export_subscriptions', methods=['POST'])
+def export_subscriptions():
+ include_muted = request.values.get('include_muted') == 'on'
+ with open_database() as connection:
+ with connection as cursor:
+ sub_list = []
+ for channel_name, channel_id, muted in (
+ _get_subscribed_channels(cursor)):
+ if muted and not include_muted:
+ continue
+ if request.values['export_format'] == 'json':
+ sub_list.append({
+ 'kind': 'youtube#subscription',
+ 'snippet': {
+ 'muted': bool(muted),
+ 'resourceId': {
+ 'channelId': channel_id,
+ 'kind': 'youtube#channel',
+ },
+ 'tags': _get_tags(cursor, channel_id),
+ 'title': channel_name,
+ },
+ })
+ elif request.values['export_format'] == 'opml':
+ sub_list.append({
+ 'channel_name': channel_name,
+ 'channel_id': channel_id,
+ })
+ if request.values['export_format'] == 'json':
+ r = flask.Response(json.dumps(sub_list), mimetype='text/json')
+ cd = 'attachment; filename="subscriptions.json"'
+ r.headers['Content-Disposition'] = cd
+ return r
+ elif request.values['export_format'] == 'opml':
+ r = flask.Response(
+ flask.render_template('subscriptions.xml', sub_list=sub_list),
+ mimetype='text/xml')
+ cd = 'attachment; filename="subscriptions.xml"'
+ r.headers['Content-Disposition'] = cd
+ return r
+ else:
+ return '400 Bad Request', 400
+
+
@yt_app.route('/subscription_manager', methods=['GET'])
def get_subscription_manager_page():
group_by_tags = request.args.get('group_by_tags', '0') == '1'
diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html
index 92cd024..62a8bed 100644
--- a/youtube/templates/subscription_manager.html
+++ b/youtube/templates/subscription_manager.html
@@ -19,14 +19,24 @@
<div class="import-export">
<form class="subscriptions-import-form" enctype="multipart/form-data" action="/youtube.com/import_subscriptions" method="POST">
<h2>Import subscriptions</h2>
- <input type="file" id="subscriptions-import" accept="application/json, application/xml, text/x-opml" name="subscriptions_file">
- <input type="submit" value="Import" class="import-submit-button">
+ <div class="subscriptions-import-options">
+ <input type="file" id="subscriptions-import" accept="application/json, application/xml, text/x-opml" name="subscriptions_file">
+ <input type="submit" value="Import">
+ </div>
</form>
- <!--<ul class="subscriptions-export-links">
- <li><a href="/youtube.com/subscriptions.opml">Export subscriptions (OPML)</a></li>
- <li><a href="/youtube.com/subscriptions.xml">Export subscriptions (RSS)</a></li>
- </ul>-->
+ <form class="subscriptions-export-form" action="/youtube.com/export_subscriptions" method="POST">
+ <h2>Export subscriptions</h2>
+ <div class="subscriptions-export-options">
+ <select id="export-type" name="export_format" title="Export format">
+ <option value="json">JSON</option>
+ <option value="opml">OPML (RSS, no tags)</option>
+ </select>
+ <label for="include-muted">Include muted</label>
+ <input id="include-muted" type="checkbox" name="include_muted" checked>
+ <input type="submit" value="Export">
+ </div>
+ </form>
</div>
<hr>
diff --git a/youtube/templates/subscriptions.xml b/youtube/templates/subscriptions.xml
new file mode 100644
index 0000000..5365da1
--- /dev/null
+++ b/youtube/templates/subscriptions.xml
@@ -0,0 +1,9 @@
+<opml version="1.1">
+ <body>
+ <outline text="YouTube Subscriptions" title="YouTube Subscriptions">
+ {% for sub in sub_list %}
+ <outline text="{{sub['channel_name']}}" title="{{sub['channel_name']}}" type="rss" xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id={{sub['channel_id']}}" />
+ {%- endfor %}
+ </outline>
+ </body>
+</opml>