aboutsummaryrefslogtreecommitdiffstats
path: root/youtube
diff options
context:
space:
mode:
authorJames Taylor <user234683@users.noreply.github.com>2021-09-01 19:13:40 -0700
committerJesús <heckyel@hyperbola.info>2021-09-01 22:01:05 -0500
commitcd3383e6e3aaf6ad4b014f16bdd9725f203a6924 (patch)
treeef849a7e9d927712c878cf72b9556ccb1b7adb52 /youtube
parentfc0fa9aaba9caa0b849a91f1c989e52ab4a7ca15 (diff)
downloadyt-local-cd3383e6e3aaf6ad4b014f16bdd9725f203a6924.tar.lz
yt-local-cd3383e6e3aaf6ad4b014f16bdd9725f203a6924.tar.xz
yt-local-cd3383e6e3aaf6ad4b014f16bdd9725f203a6924.zip
Add NewPipe subscriptions import and export
Closes #82 Signed-off-by: Jesús <heckyel@hyperbola.info>
Diffstat (limited to 'youtube')
-rw-r--r--youtube/subscriptions.py57
-rw-r--r--youtube/templates/subscription_manager.html3
2 files changed, 49 insertions, 11 deletions
diff --git a/youtube/subscriptions.py b/youtube/subscriptions.py
index 6ab12c7..7a2bf7d 100644
--- a/youtube/subscriptions.py
+++ b/youtube/subscriptions.py
@@ -684,7 +684,7 @@ def check_specific_channels(channel_ids):
channel_names.update(channel_id_name_list)
check_channels_if_necessary(channel_ids)
-
+CHANNEL_ID_RE = re.compile(r'UC[-_\w]{22}')
@yt_app.route('/import_subscriptions', methods=['POST'])
def import_subscriptions():
@@ -702,15 +702,36 @@ def import_subscriptions():
mime_type = file.mimetype
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:
- file = json.loads(file)
+ info = json.loads(info)
except json.decoder.JSONDecodeError:
traceback.print_exc()
return '400 Bad Request: Invalid json file', 400
+ channels = []
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):
traceback.print_exc()
return '400 Bad Request: Unknown json structure', 400
@@ -738,8 +759,7 @@ def import_subscriptions():
for row in reader:
if not row or row[0].lower().strip() == 'channel id':
continue
- elif len(row) > 1 and re.fullmatch(r'UC[-_\w]{22}',
- row[0].strip()):
+ elif len(row) > 1 and CHANNEL_ID_RE.fullmatch(row[0].strip()):
channels.append( (row[0], row[-1]) )
else:
print('WARNING: Unknown row format:', row)
@@ -767,7 +787,7 @@ def export_subscriptions():
_get_subscribed_channels(cursor)):
if muted and not include_muted:
continue
- if request.values['export_format'] == 'json':
+ if request.values['export_format'] == 'json_google_takeout':
sub_list.append({
'kind': 'youtube#subscription',
'snippet': {
@@ -780,21 +800,38 @@ def export_subscriptions():
'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':
sub_list.append({
'channel_name': channel_name,
'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')
- 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
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"'
+ cd = 'attachment; filename="subscriptions_%s.xml"' % date_time
r.headers['Content-Disposition'] = cd
return r
else:
diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html
index 5e240b2..cf51a9b 100644
--- a/youtube/templates/subscription_manager.html
+++ b/youtube/templates/subscription_manager.html
@@ -29,7 +29,8 @@
<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="json_newpipe">JSON (NewPipe)</option>
+ <option value="json_google_takeout">JSON (Old Google Takeout Format)</option>
<option value="opml">OPML (RSS, no tags)</option>
</select>
<label for="include-muted">Include muted</label>