API extensions, #build

Changed:
- [API] Added endpoints for subscription toggle
- [API] Added endpoint for playlist delete
- Trigger bgsave when storing redis config
- Validate subscribe url Type, surface errors
- ignore eaDir folder
This commit is contained in:
Simon 2023-08-24 00:06:54 +07:00
commit 15794ebfc8
No known key found for this signature in database
GPG Key ID: 2C15AA5E89985DD4
17 changed files with 160 additions and 113 deletions

View File

@ -2,6 +2,10 @@
from api.src.search_processor import SearchProcess from api.src.search_processor import SearchProcess
from home.src.download.queue import PendingInteract from home.src.download.queue import PendingInteract
from home.src.download.subscriptions import (
ChannelSubscription,
PlaylistSubscription,
)
from home.src.download.yt_dlp_base import CookieHandler from home.src.download.yt_dlp_base import CookieHandler
from home.src.es.connect import ElasticWrap from home.src.es.connect import ElasticWrap
from home.src.es.snapshot import ElasticSnapshot from home.src.es.snapshot import ElasticSnapshot
@ -9,6 +13,7 @@ from home.src.frontend.searching import SearchForm
from home.src.frontend.watched import WatchState from home.src.frontend.watched import WatchState
from home.src.index.channel import YoutubeChannel from home.src.index.channel import YoutubeChannel
from home.src.index.generic import Pagination from home.src.index.generic import Pagination
from home.src.index.playlist import YoutubePlaylist
from home.src.index.reindex import ReindexProgress from home.src.index.reindex import ReindexProgress
from home.src.index.video import SponsorBlock, YoutubeVideo from home.src.index.video import SponsorBlock, YoutubeVideo
from home.src.ta.config import AppConfig, ReleaseVersion from home.src.ta.config import AppConfig, ReleaseVersion
@ -318,9 +323,8 @@ class ChannelApiListView(ApiBaseView):
return Response(self.response) return Response(self.response)
@staticmethod def post(self, request):
def post(request): """subscribe/unsubscribe to list of channels"""
"""subscribe to list of channels"""
data = request.data data = request.data
try: try:
to_add = data["data"] to_add = data["data"]
@ -329,12 +333,28 @@ class ChannelApiListView(ApiBaseView):
print(message) print(message)
return Response({"message": message}, status=400) return Response({"message": message}, status=400)
pending = [i["channel_id"] for i in to_add if i["channel_subscribed"]] pending = []
url_str = " ".join(pending) for channel_item in to_add:
subscribe_to.delay(url_str) channel_id = channel_item["channel_id"]
if channel_item["channel_subscribed"]:
pending.append(channel_id)
else:
self._unsubscribe(channel_id)
if pending:
url_str = " ".join(pending)
subscribe_to.delay(url_str, expected_type="channel")
return Response(data) return Response(data)
@staticmethod
def _unsubscribe(channel_id: str):
"""unsubscribe"""
print(f"[{channel_id}] unsubscribe from channel")
ChannelSubscription().change_subscribe(
channel_id, channel_subscribed=False
)
class ChannelApiVideoView(ApiBaseView): class ChannelApiVideoView(ApiBaseView):
"""resolves to /api/channel/<channel-id>/video """resolves to /api/channel/<channel-id>/video
@ -373,6 +393,38 @@ class PlaylistApiListView(ApiBaseView):
self.get_document_list(request) self.get_document_list(request)
return Response(self.response) return Response(self.response)
def post(self, request):
"""subscribe/unsubscribe to list of playlists"""
data = request.data
try:
to_add = data["data"]
except KeyError:
message = "missing expected data key"
print(message)
return Response({"message": message}, status=400)
pending = []
for playlist_item in to_add:
playlist_id = playlist_item["playlist_id"]
if playlist_item["playlist_subscribed"]:
pending.append(playlist_id)
else:
self._unsubscribe(playlist_id)
if pending:
url_str = " ".join(pending)
subscribe_to.delay(url_str, expected_type="playlist")
return Response(data)
@staticmethod
def _unsubscribe(playlist_id: str):
"""unsubscribe"""
print(f"[{playlist_id}] unsubscribe from playlist")
PlaylistSubscription().change_subscribe(
playlist_id, subscribe_status=False
)
class PlaylistApiView(ApiBaseView): class PlaylistApiView(ApiBaseView):
"""resolves to /api/playlist/<playlist_id>/ """resolves to /api/playlist/<playlist_id>/
@ -387,6 +439,17 @@ class PlaylistApiView(ApiBaseView):
self.get_document(playlist_id) self.get_document(playlist_id)
return Response(self.response, status=self.status_code) return Response(self.response, status=self.status_code)
def delete(self, request, playlist_id):
"""delete playlist"""
print(f"{playlist_id}: delete playlist")
delete_videos = request.GET.get("delete-videos", False)
if delete_videos:
YoutubePlaylist(playlist_id).delete_videos_playlist()
else:
YoutubePlaylist(playlist_id).delete_metadata()
return Response({"success": True})
class PlaylistApiVideoView(ApiBaseView): class PlaylistApiVideoView(ApiBaseView):
"""resolves to /api/playlist/<playlist_id>/video """resolves to /api/playlist/<playlist_id>/video

View File

@ -332,7 +332,7 @@ class SubscriptionHandler:
self.task = task self.task = task
self.to_subscribe = False self.to_subscribe = False
def subscribe(self): def subscribe(self, expected_type=False):
"""subscribe to url_str items""" """subscribe to url_str items"""
if self.task: if self.task:
self.task.send_progress(["Processing form content."]) self.task.send_progress(["Processing form content."])
@ -343,11 +343,16 @@ class SubscriptionHandler:
if self.task: if self.task:
self._notify(idx, item, total) self._notify(idx, item, total)
self.subscribe_type(item) self.subscribe_type(item, expected_type=expected_type)
def subscribe_type(self, item): def subscribe_type(self, item, expected_type):
"""process single item""" """process single item"""
if item["type"] == "playlist": if item["type"] == "playlist":
if expected_type and expected_type != "playlist":
raise TypeError(
f"expected {expected_type} url but got {item.get('type')}"
)
PlaylistSubscription().process_url_str([item]) PlaylistSubscription().process_url_str([item])
return return
@ -360,6 +365,11 @@ class SubscriptionHandler:
else: else:
raise ValueError("failed to subscribe to: " + item["url"]) raise ValueError("failed to subscribe to: " + item["url"])
if expected_type and expected_type != "channel":
raise TypeError(
f"expected {expected_type} url but got {item.get('type')}"
)
self._subscribe(channel_id) self._subscribe(channel_id)
def _subscribe(self, channel_id): def _subscribe(self, channel_id):

View File

@ -61,7 +61,7 @@ class ThumbManagerBase:
print(f"{self.item_id}: retry thumbnail download {url}") print(f"{self.item_id}: retry thumbnail download {url}")
sleep((i + 1) ** i) sleep((i + 1) ** i)
return False return self.get_fallback()
def get_fallback(self): def get_fallback(self):
"""get fallback thumbnail if not available""" """get fallback thumbnail if not available"""

View File

@ -112,9 +112,9 @@ class CookieHandler:
def set_cookie(self, cookie): def set_cookie(self, cookie):
"""set cookie str and activate in cofig""" """set cookie str and activate in cofig"""
RedisArchivist().set_message("cookie", cookie) RedisArchivist().set_message("cookie", cookie, save=True)
path = ".downloads.cookie_import" path = ".downloads.cookie_import"
RedisArchivist().set_message("config", True, path=path) RedisArchivist().set_message("config", True, path=path, save=True)
self.config["downloads"]["cookie_import"] = True self.config["downloads"]["cookie_import"] = True
print("cookie: activated and stored in Redis") print("cookie: activated and stored in Redis")

View File

@ -4,14 +4,8 @@ Functionality:
- called via user input - called via user input
""" """
from home.src.download.subscriptions import (
ChannelSubscription,
PlaylistSubscription,
)
from home.src.index.playlist import YoutubePlaylist
from home.src.ta.ta_redis import RedisArchivist from home.src.ta.ta_redis import RedisArchivist
from home.src.ta.urlparser import Parser from home.tasks import run_restore_backup
from home.tasks import run_restore_backup, subscribe_to
class PostData: class PostData:
@ -36,14 +30,11 @@ class PostData:
exec_map = { exec_map = {
"change_view": self._change_view, "change_view": self._change_view,
"change_grid": self._change_grid, "change_grid": self._change_grid,
"unsubscribe": self._unsubscribe,
"subscribe": self._subscribe,
"sort_order": self._sort_order, "sort_order": self._sort_order,
"hide_watched": self._hide_watched, "hide_watched": self._hide_watched,
"show_subed_only": self._show_subed_only, "show_subed_only": self._show_subed_only,
"show_ignored_only": self._show_ignored_only, "show_ignored_only": self._show_ignored_only,
"db-restore": self._db_restore, "db-restore": self._db_restore,
"delete-playlist": self._delete_playlist,
} }
return exec_map[self.to_exec] return exec_map[self.to_exec]
@ -67,34 +58,6 @@ class PostData:
RedisArchivist().set_message(key, {"status": grid_items}) RedisArchivist().set_message(key, {"status": grid_items})
return {"success": True} return {"success": True}
def _unsubscribe(self):
"""unsubscribe from channels or playlists"""
id_unsub = self.exec_val
print(f"{id_unsub}: unsubscribe")
to_unsub_list = Parser(id_unsub).parse()
for to_unsub in to_unsub_list:
unsub_type = to_unsub["type"]
unsub_id = to_unsub["url"]
if unsub_type == "playlist":
PlaylistSubscription().change_subscribe(
unsub_id, subscribe_status=False
)
elif unsub_type == "channel":
ChannelSubscription().change_subscribe(
unsub_id, channel_subscribed=False
)
else:
raise ValueError("failed to process " + id_unsub)
return {"success": True}
def _subscribe(self):
"""subscribe to channel or playlist, called from js buttons"""
id_sub = self.exec_val
print(f"{id_sub}: subscribe")
subscribe_to.delay(id_sub)
return {"success": True}
def _sort_order(self): def _sort_order(self):
"""change the sort between published to downloaded""" """change the sort between published to downloaded"""
sort_order = {"status": self.exec_val} sort_order = {"status": self.exec_val}
@ -139,16 +102,3 @@ class PostData:
filename = self.exec_val filename = self.exec_val
run_restore_backup.delay(filename) run_restore_backup.delay(filename)
return {"success": True} return {"success": True}
def _delete_playlist(self):
"""delete playlist, only metadata or incl all videos"""
playlist_dict = self.exec_val
playlist_id = playlist_dict["playlist-id"]
playlist_action = playlist_dict["playlist-action"]
print(f"{playlist_id}: delete playlist {playlist_action}")
if playlist_action == "metadata":
YoutubePlaylist(playlist_id).delete_metadata()
elif playlist_action == "all":
YoutubePlaylist(playlist_id).delete_videos_playlist()
return {"success": True}

View File

@ -100,7 +100,7 @@ class AppConfig:
self.config[config_dict][config_value] = to_write self.config[config_dict][config_value] = to_write
updated.append((config_value, to_write)) updated.append((config_value, to_write))
RedisArchivist().set_message("config", self.config) RedisArchivist().set_message("config", self.config, save=True)
return updated return updated
@staticmethod @staticmethod
@ -112,7 +112,7 @@ class AppConfig:
message = {"status": value} message = {"status": value}
redis_key = f"{user_id}:{key}" redis_key = f"{user_id}:{key}"
RedisArchivist().set_message(redis_key, message) RedisArchivist().set_message(redis_key, message, save=True)
def get_colors(self): def get_colors(self):
"""overwrite config if user has set custom values""" """overwrite config if user has set custom values"""
@ -225,7 +225,7 @@ class ScheduleBuilder:
to_write = value to_write = value
redis_config["scheduler"][key] = to_write redis_config["scheduler"][key] = to_write
RedisArchivist().set_message("config", redis_config) RedisArchivist().set_message("config", redis_config, save=True)
mess_dict = { mess_dict = {
"status": self.MSG, "status": self.MSG,
"level": "info", "level": "info",

View File

@ -15,7 +15,12 @@ import requests
def ignore_filelist(filelist: list[str]) -> list[str]: def ignore_filelist(filelist: list[str]) -> list[str]:
"""ignore temp files for os.listdir sanitizer""" """ignore temp files for os.listdir sanitizer"""
to_ignore = ["Icon\r\r", "Temporary Items", "Network Trash Folder"] to_ignore = [
"@eaDir",
"Icon\r\r",
"Network Trash Folder",
"Temporary Items",
]
cleaned: list[str] = [] cleaned: list[str] = []
for file_name in filelist: for file_name in filelist:
if file_name.startswith(".") or file_name in to_ignore: if file_name.startswith(".") or file_name in to_ignore:
@ -110,7 +115,7 @@ def clear_dl_cache(config: dict) -> int:
"""clear leftover files from dl cache""" """clear leftover files from dl cache"""
print("clear download cache") print("clear download cache")
cache_dir = os.path.join(config["application"]["cache_dir"], "download") cache_dir = os.path.join(config["application"]["cache_dir"], "download")
leftover_files = os.listdir(cache_dir) leftover_files = ignore_filelist(os.listdir(cache_dir))
for cached in leftover_files: for cached in leftover_files:
to_delete = os.path.join(cache_dir, cached) to_delete = os.path.join(cache_dir, cached)
os.remove(to_delete) os.remove(to_delete)

View File

@ -41,6 +41,7 @@ class RedisArchivist(RedisBase):
message: dict, message: dict,
path: str = ".", path: str = ".",
expire: bool | int = False, expire: bool | int = False,
save: bool = False,
) -> None: ) -> None:
"""write new message to redis""" """write new message to redis"""
self.conn.execute_command( self.conn.execute_command(
@ -54,6 +55,16 @@ class RedisArchivist(RedisBase):
secs = expire secs = expire
self.conn.execute_command("EXPIRE", self.NAME_SPACE + key, secs) self.conn.execute_command("EXPIRE", self.NAME_SPACE + key, secs)
if save:
self.bg_save()
def bg_save(self) -> None:
"""save to aof"""
try:
self.conn.bgsave()
except redis.exceptions.ResponseError:
pass
def get_message(self, key: str) -> dict: def get_message(self, key: str) -> dict:
"""get message dict from redis""" """get message dict from redis"""
reply = self.conn.execute_command("JSON.GET", self.NAME_SPACE + key) reply = self.conn.execute_command("JSON.GET", self.NAME_SPACE + key)

View File

@ -343,9 +343,12 @@ def re_sync_thumbs(self):
@shared_task(bind=True, name="subscribe_to", base=BaseTask) @shared_task(bind=True, name="subscribe_to", base=BaseTask)
def subscribe_to(self, url_str): def subscribe_to(self, url_str: str, expected_type: str | bool = False):
"""take a list of urls to subscribe to""" """
SubscriptionHandler(url_str, task=self).subscribe() take a list of urls to subscribe to
optionally validate expected_type channel / playlist
"""
SubscriptionHandler(url_str, task=self).subscribe(expected_type)
@shared_task(bind=True, name="index_playlists", base=BaseTask) @shared_task(bind=True, name="index_playlists", base=BaseTask)

View File

@ -66,9 +66,9 @@
<div> <div>
<p>Last refreshed: {{ channel.source.channel_last_refresh }}</p> <p>Last refreshed: {{ channel.source.channel_last_refresh }}</p>
{% if channel.source.channel_subscribed %} {% if channel.source.channel_subscribed %}
<button class="unsubscribe" type="button" id="{{ channel.source.channel_id }}" onclick="unsubscribe(this.id)" title="Unsubscribe from {{ channel.source.channel_name }}">Unsubscribe</button> <button class="unsubscribe" type="button" data-type="channel" data-subscribe="" data-id="{{ channel.source.channel_id }}" onclick="subscribeStatus(this)" title="Unsubscribe from {{ channel.source.channel_name }}">Unsubscribe</button>
{% else %} {% else %}
<button type="button" id="{{ channel.source.channel_id }}" onclick="subscribe(this.id)" title="Subscribe to {{ channel.source.channel_name }}">Subscribe</button> <button type="button" data-type="channel" data-subscribe="true" data-id="{{ channel.source.channel_id }}" onclick="subscribeStatus(this)" title="Subscribe to {{ channel.source.channel_name }}">Subscribe</button>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -38,9 +38,9 @@
<p>Subscribers: {{ channel_info.channel_subs|intcomma }}</p> <p>Subscribers: {{ channel_info.channel_subs|intcomma }}</p>
{% endif %} {% endif %}
{% if channel_info.channel_subscribed %} {% if channel_info.channel_subscribed %}
<button class="unsubscribe" type="button" id="{{ channel_info.channel_id }}" onclick="unsubscribe(this.id)" title="Unsubscribe from {{ channel_info.channel_name }}">Unsubscribe</button> <button class="unsubscribe" type="button" data-type="channel" data-subscribe="" data-id="{{ channel_info.channel_id }}" onclick="subscribeStatus(this)" title="Unsubscribe from {{ channel_info.channel_name }}">Unsubscribe</button>
{% else %} {% else %}
<button type="button" id="{{ channel_info.channel_id }}" onclick="subscribe(this.id)" title="Subscribe to {{ channel_info.channel_name }}">Subscribe</button> <button type="button" data-type="channel" data-subscribe="true" data-id="{{ channel_info.channel_id }}" onclick="subscribeStatus(this)" title="Subscribe to {{ channel_info.channel_name }}">Subscribe</button>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -54,9 +54,9 @@
<a href="{% url 'playlist_id' playlist.source.playlist_id %}"><h2>{{ playlist.source.playlist_name }}</h2></a> <a href="{% url 'playlist_id' playlist.source.playlist_id %}"><h2>{{ playlist.source.playlist_name }}</h2></a>
<p>Last refreshed: {{ playlist.source.playlist_last_refresh }}</p> <p>Last refreshed: {{ playlist.source.playlist_last_refresh }}</p>
{% if playlist.source.playlist_subscribed %} {% if playlist.source.playlist_subscribed %}
<button class="unsubscribe" type="button" id="{{ playlist.source.playlist_id }}" onclick="unsubscribe(this.id)" title="Unsubscribe from {{ playlist.source.playlist_name }}">Unsubscribe</button> <button class="unsubscribe" type="button" data-type="playlist" data-subscribe="" data-id="{{ playlist.source.playlist_id }}" onclick="subscribeStatus(this)" title="Unsubscribe from {{ playlist.source.playlist_name }}">Unsubscribe</button>
{% else %} {% else %}
<button type="button" id="{{ playlist.source.playlist_id }}" onclick="subscribe(this.id)" title="Subscribe to {{ playlist.source.playlist_name }}">Subscribe</button> <button type="button" data-type="playlist" data-subscribe="true" data-id="{{ playlist.source.playlist_id }}" onclick="subscribeStatus(this)" title="Subscribe to {{ playlist.source.playlist_name }}">Subscribe</button>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -49,9 +49,9 @@
<a href="{% url 'playlist_id' playlist.source.playlist_id %}"><h2>{{ playlist.source.playlist_name }}</h2></a> <a href="{% url 'playlist_id' playlist.source.playlist_id %}"><h2>{{ playlist.source.playlist_name }}</h2></a>
<p>Last refreshed: {{ playlist.source.playlist_last_refresh }}</p> <p>Last refreshed: {{ playlist.source.playlist_last_refresh }}</p>
{% if playlist.source.playlist_subscribed %} {% if playlist.source.playlist_subscribed %}
<button class="unsubscribe" type="button" id="{{ playlist.source.playlist_id }}" onclick="unsubscribe(this.id)" title="Unsubscribe from {{ playlist.source.playlist_name }}">Unsubscribe</button> <button class="unsubscribe" type="button" data-type="playlist" data-subscribe="" data-id="{{ playlist.source.playlist_id }}" onclick="subscribeStatus(this)" title="Unsubscribe from {{ playlist.source.playlist_name }}">Unsubscribe</button>
{% else %} {% else %}
<button type="button" id="{{ playlist.source.playlist_id }}" onclick="subscribe(this.id)" title="Subscribe to {{ playlist.source.playlist_name }}">Subscribe</button> <button type="button" data-type="playlist" data-subscribe="true" data-id="{{ playlist.source.playlist_id }}" onclick="subscribeStatus(this)" title="Subscribe to {{ playlist.source.playlist_name }}">Subscribe</button>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -27,9 +27,9 @@
<p>Last refreshed: {{ playlist_info.playlist_last_refresh }}</p> <p>Last refreshed: {{ playlist_info.playlist_last_refresh }}</p>
<p>Playlist: <p>Playlist:
{% if playlist_info.playlist_subscribed %} {% if playlist_info.playlist_subscribed %}
<button class="unsubscribe" type="button" id="{{ playlist_info.playlist_id }}" onclick="unsubscribe(this.id)" title="Unsubscribe from {{ playlist_info.playlist_name }}">Unsubscribe</button> <button class="unsubscribe" type="button" data-type="playlist" data-subscribe="" data-id="{{ playlist_info.playlist_id }}" onclick="subscribeStatus(this)" title="Unsubscribe from {{ playlist_info.playlist_name }}">Unsubscribe</button>
{% else %} {% else %}
<button type="button" id="{{ playlist_info.playlist_id }}" onclick="subscribe(this.id)" title="Subscribe to {{ playlist_info.playlist_name }}">Subscribe</button> <button type="button" data-type="playlist" data-subscribe="true" data-id="{{ playlist_info.playlist_id }}" onclick="subscribeStatus(this)" title="Subscribe to {{ playlist_info.playlist_name }}">Subscribe</button>
{% endif %} {% endif %}
</p> </p>
{% if playlist_info.playlist_active %} {% if playlist_info.playlist_active %}
@ -40,8 +40,8 @@
<button onclick="deleteConfirm()" id="delete-item">Delete Playlist</button> <button onclick="deleteConfirm()" id="delete-item">Delete Playlist</button>
<div class="delete-confirm" id="delete-button"> <div class="delete-confirm" id="delete-button">
<span>Delete {{ playlist_info.playlist_name }}?</span> <span>Delete {{ playlist_info.playlist_name }}?</span>
<button onclick="deletePlaylist(this)" data-action="metadata" data-id="{{ playlist_info.playlist_id }}">Delete metadata</button> <button onclick="deletePlaylist(this)" data-action="" data-id="{{ playlist_info.playlist_id }}">Delete metadata</button>
<button onclick="deletePlaylist(this)" data-action="all" class="danger-button" data-id="{{ playlist_info.playlist_id }}">Delete all</button><br> <button onclick="deletePlaylist(this)" data-action="delete-videos" class="danger-button" data-id="{{ playlist_info.playlist_id }}">Delete all</button><br>
<button onclick="cancelDelete()">Cancel</button> <button onclick="cancelDelete()">Cancel</button>
</div> </div>
</div> </div>

View File

@ -736,7 +736,7 @@ class ChannelView(ArchivistResultsView):
if subscribe_form.is_valid(): if subscribe_form.is_valid():
url_str = request.POST.get("subscribe") url_str = request.POST.get("subscribe")
print(url_str) print(url_str)
subscribe_to.delay(url_str) subscribe_to.delay(url_str, expected_type="channel")
sleep(1) sleep(1)
return redirect("channel", permanent=True) return redirect("channel", permanent=True)
@ -879,7 +879,7 @@ class PlaylistView(ArchivistResultsView):
if subscribe_form.is_valid(): if subscribe_form.is_valid():
url_str = request.POST.get("subscribe") url_str = request.POST.get("subscribe")
print(url_str) print(url_str)
subscribe_to.delay(url_str) subscribe_to.delay(url_str, expected_type="playlist")
sleep(1) sleep(1)
return redirect("playlist") return redirect("playlist")

View File

@ -1,13 +1,13 @@
apprise==1.4.5 apprise==1.4.5
celery==5.3.1 celery==5.3.1
Django==4.2.3 Django==4.2.4
django-auth-ldap==4.4.0 django-auth-ldap==4.5.0
django-cors-headers==4.2.0 django-cors-headers==4.2.0
djangorestframework==3.14.0 djangorestframework==3.14.0
Pillow==10.0.0 Pillow==10.0.0
redis==4.6.0 redis==5.0.0
requests==2.31.0 requests==2.31.0
ryd-client==0.0.6 ryd-client==0.0.6
uWSGI==2.0.21 uWSGI==2.0.22
whitenoise==6.5.0 whitenoise==6.5.0
yt_dlp==2023.7.6 yt_dlp==2023.7.6

View File

@ -71,20 +71,27 @@ function isWatchedButton(button) {
}, 1000); }, 1000);
} }
function unsubscribe(id_unsub) { function subscribeStatus(subscribeButton) {
let payload = JSON.stringify({ unsubscribe: id_unsub }); let id = subscribeButton.getAttribute('data-id');
sendPost(payload); let type = subscribeButton.getAttribute('data-type');
let subscribe = Boolean(subscribeButton.getAttribute('data-subscribe'));
let apiEndpoint;
let data;
if (type === 'channel') {
apiEndpoint = '/api/channel/';
data = { data: [{ channel_id: id, channel_subscribed: subscribe }] };
} else if (type === 'playlist') {
apiEndpoint = '/api/playlist/';
data = { data: [{ playlist_id: id, playlist_subscribed: subscribe }] };
}
apiRequest(apiEndpoint, 'POST', data);
let message = document.createElement('span'); let message = document.createElement('span');
message.innerText = 'You are unsubscribed.'; if (subscribe) {
document.getElementById(id_unsub).replaceWith(message); message.innerText = 'You are subscribed.';
} } else {
message.innerText = 'You are unsubscribed.';
function subscribe(id_sub) { }
let payload = JSON.stringify({ subscribe: id_sub }); subscribeButton.replaceWith(message);
sendPost(payload);
let message = document.createElement('span');
message.innerText = 'You are subscribed.';
document.getElementById(id_sub).replaceWith(message);
} }
function changeView(image) { function changeView(image) {
@ -374,13 +381,11 @@ function deleteChannel(button) {
function deletePlaylist(button) { function deletePlaylist(button) {
let playlist_id = button.getAttribute('data-id'); let playlist_id = button.getAttribute('data-id');
let playlist_action = button.getAttribute('data-action'); let playlist_action = button.getAttribute('data-action');
let payload = JSON.stringify({ let apiEndpoint = `/api/playlist/${playlist_id}/`;
'delete-playlist': { if (playlist_action === 'delete-videos') {
'playlist-id': playlist_id, apiEndpoint += '?delete-videos=true';
'playlist-action': playlist_action, }
}, apiRequest(apiEndpoint, 'DELETE');
});
sendPost(payload);
setTimeout(function () { setTimeout(function () {
window.location.replace('/playlist/'); window.location.replace('/playlist/');
}, 1000); }, 1000);
@ -1057,9 +1062,9 @@ function createChannel(channel, viewStyle) {
const channelLastRefresh = channel.channel_last_refresh; const channelLastRefresh = channel.channel_last_refresh;
let button; let button;
if (channel.channel_subscribed) { if (channel.channel_subscribed) {
button = `<button class="unsubscribe" type="button" id="${channelId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${channelName}">Unsubscribe</button>`; button = `<button class="unsubscribe" type="button" data-id="${channelId}" data-subscribe="" data-type="channel" onclick="subscribeStatus(this)" title="Unsubscribe from ${channelName}">Unsubscribe</button>`;
} else { } else {
button = `<button type="button" id="${channelId}" onclick="subscribe(this.id)" title="Subscribe to ${channelName}">Subscribe</button>`; button = `<button type="button" data-id="${channelId}" data-subscribe="true" data-type="channel" onclick="subscribeStatus(this)" title="Subscribe to ${channelName}">Subscribe</button>`;
} }
// build markup // build markup
const markup = ` const markup = `
@ -1103,9 +1108,9 @@ function createPlaylist(playlist, viewStyle) {
const playlistLastRefresh = playlist.playlist_last_refresh; const playlistLastRefresh = playlist.playlist_last_refresh;
let button; let button;
if (playlist.playlist_subscribed) { if (playlist.playlist_subscribed) {
button = `<button class="unsubscribe" type="button" id="${playlistId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${playlistName}">Unsubscribe</button>`; button = `<button class="unsubscribe" type="button" data-id="${playlistId}" data-subscribe="" data-type="playlist" onclick="subscribeStatus(this)" title="Unsubscribe from ${playlistName}">Unsubscribe</button>`;
} else { } else {
button = `<button type="button" id="${playlistId}" onclick="subscribe(this.id)" title="Subscribe to ${playlistName}">Subscribe</button>`; button = `<button type="button" data-id="${playlistId}" data-subscribe="true" data-type="playlist" onclick="subscribeStatus(this)" title="Subscribe to ${playlistName}">Subscribe</button>`;
} }
const markup = ` const markup = `
<div class="playlist-thumbnail"> <div class="playlist-thumbnail">