Add NewPipe subscriptions import and export
Closes #82 Signed-off-by: Jesús <heckyel@hyperbola.info>
This commit is contained in:
parent
fc0fa9aaba
commit
cd3383e6e3
@ -684,7 +684,7 @@ def check_specific_channels(channel_ids):
|
|||||||
channel_names.update(channel_id_name_list)
|
channel_names.update(channel_id_name_list)
|
||||||
check_channels_if_necessary(channel_ids)
|
check_channels_if_necessary(channel_ids)
|
||||||
|
|
||||||
|
CHANNEL_ID_RE = re.compile(r'UC[-_\w]{22}')
|
||||||
@yt_app.route('/import_subscriptions', methods=['POST'])
|
@yt_app.route('/import_subscriptions', methods=['POST'])
|
||||||
def import_subscriptions():
|
def import_subscriptions():
|
||||||
|
|
||||||
@ -702,15 +702,36 @@ def import_subscriptions():
|
|||||||
mime_type = file.mimetype
|
mime_type = file.mimetype
|
||||||
|
|
||||||
if mime_type == 'application/json':
|
if mime_type == 'application/json':
|
||||||
file = file.read().decode('utf-8')
|
info = file.read().decode('utf-8')
|
||||||
|
if info == '':
|
||||||
|
return '400 Bad Request: File is empty', 400
|
||||||
try:
|
try:
|
||||||
file = json.loads(file)
|
info = json.loads(info)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return '400 Bad Request: Invalid json file', 400
|
return '400 Bad Request: Invalid json file', 400
|
||||||
|
|
||||||
|
channels = []
|
||||||
try:
|
try:
|
||||||
channels = ((item['snippet']['resourceId']['channelId'], item['snippet']['title']) for item in file)
|
if 'app_version_int' in info: # NewPipe Format
|
||||||
|
for item in info['subscriptions']:
|
||||||
|
# Other service, such as SoundCloud
|
||||||
|
if item.get('service_id', 0) != 0:
|
||||||
|
continue
|
||||||
|
channel_url = item['url']
|
||||||
|
channel_id_match = CHANNEL_ID_RE.search(channel_url)
|
||||||
|
if channel_id_match:
|
||||||
|
channel_id = channel_id_match.group(0)
|
||||||
|
else:
|
||||||
|
print('WARNING: Could not find channel id in url',
|
||||||
|
channel_url)
|
||||||
|
continue
|
||||||
|
channels.append((channel_id, item['name']))
|
||||||
|
else: # Old Google Takeout format
|
||||||
|
for item in info:
|
||||||
|
snippet = item['snippet']
|
||||||
|
channel_id = snippet['resourceId']['channelId']
|
||||||
|
channels.append((channel_id, snippet['title']))
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return '400 Bad Request: Unknown json structure', 400
|
return '400 Bad Request: Unknown json structure', 400
|
||||||
@ -738,8 +759,7 @@ def import_subscriptions():
|
|||||||
for row in reader:
|
for row in reader:
|
||||||
if not row or row[0].lower().strip() == 'channel id':
|
if not row or row[0].lower().strip() == 'channel id':
|
||||||
continue
|
continue
|
||||||
elif len(row) > 1 and re.fullmatch(r'UC[-_\w]{22}',
|
elif len(row) > 1 and CHANNEL_ID_RE.fullmatch(row[0].strip()):
|
||||||
row[0].strip()):
|
|
||||||
channels.append( (row[0], row[-1]) )
|
channels.append( (row[0], row[-1]) )
|
||||||
else:
|
else:
|
||||||
print('WARNING: Unknown row format:', row)
|
print('WARNING: Unknown row format:', row)
|
||||||
@ -767,7 +787,7 @@ def export_subscriptions():
|
|||||||
_get_subscribed_channels(cursor)):
|
_get_subscribed_channels(cursor)):
|
||||||
if muted and not include_muted:
|
if muted and not include_muted:
|
||||||
continue
|
continue
|
||||||
if request.values['export_format'] == 'json':
|
if request.values['export_format'] == 'json_google_takeout':
|
||||||
sub_list.append({
|
sub_list.append({
|
||||||
'kind': 'youtube#subscription',
|
'kind': 'youtube#subscription',
|
||||||
'snippet': {
|
'snippet': {
|
||||||
@ -780,21 +800,38 @@ def export_subscriptions():
|
|||||||
'title': channel_name,
|
'title': channel_name,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
elif request.values['export_format'] == 'json_newpipe':
|
||||||
|
sub_list.append({
|
||||||
|
'service_id': 0,
|
||||||
|
'url': 'https://www.youtube.com/channel/' + channel_id,
|
||||||
|
'name': channel_name,
|
||||||
|
})
|
||||||
elif request.values['export_format'] == 'opml':
|
elif request.values['export_format'] == 'opml':
|
||||||
sub_list.append({
|
sub_list.append({
|
||||||
'channel_name': channel_name,
|
'channel_name': channel_name,
|
||||||
'channel_id': channel_id,
|
'channel_id': channel_id,
|
||||||
})
|
})
|
||||||
if request.values['export_format'] == 'json':
|
date_time = time.strftime('%Y%m%d%H%M', time.localtime())
|
||||||
|
if request.values['export_format'] == 'json_google_takeout':
|
||||||
r = flask.Response(json.dumps(sub_list), mimetype='text/json')
|
r = flask.Response(json.dumps(sub_list), mimetype='text/json')
|
||||||
cd = 'attachment; filename="subscriptions.json"'
|
cd = 'attachment; filename="subscriptions_%s.json"' % date_time
|
||||||
|
r.headers['Content-Disposition'] = cd
|
||||||
|
return r
|
||||||
|
elif request.values['export_format'] == 'json_newpipe':
|
||||||
|
r = flask.Response(json.dumps({
|
||||||
|
'app_version': '0.21.9',
|
||||||
|
'app_version_int': 975,
|
||||||
|
'subscriptions': sub_list,
|
||||||
|
}), mimetype='text/json')
|
||||||
|
file_name = 'newpipe_subscriptions_%s_youtube-local.json' % date_time
|
||||||
|
cd = 'attachment; filename="%s"' % file_name
|
||||||
r.headers['Content-Disposition'] = cd
|
r.headers['Content-Disposition'] = cd
|
||||||
return r
|
return r
|
||||||
elif request.values['export_format'] == 'opml':
|
elif request.values['export_format'] == 'opml':
|
||||||
r = flask.Response(
|
r = flask.Response(
|
||||||
flask.render_template('subscriptions.xml', sub_list=sub_list),
|
flask.render_template('subscriptions.xml', sub_list=sub_list),
|
||||||
mimetype='text/xml')
|
mimetype='text/xml')
|
||||||
cd = 'attachment; filename="subscriptions.xml"'
|
cd = 'attachment; filename="subscriptions_%s.xml"' % date_time
|
||||||
r.headers['Content-Disposition'] = cd
|
r.headers['Content-Disposition'] = cd
|
||||||
return r
|
return r
|
||||||
else:
|
else:
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
<h2>Export subscriptions</h2>
|
<h2>Export subscriptions</h2>
|
||||||
<div class="subscriptions-export-options">
|
<div class="subscriptions-export-options">
|
||||||
<select id="export-type" name="export_format" title="Export format">
|
<select id="export-type" name="export_format" title="Export format">
|
||||||
<option value="json">JSON</option>
|
<option value="json_newpipe">JSON (NewPipe)</option>
|
||||||
|
<option value="json_google_takeout">JSON (Old Google Takeout Format)</option>
|
||||||
<option value="opml">OPML (RSS, no tags)</option>
|
<option value="opml">OPML (RSS, no tags)</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="include-muted">Include muted</label>
|
<label for="include-muted">Include muted</label>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user