mirror of
https://github.com/tubearchivist/tubearchivist
synced 2024-11-19 15:25:51 +00:00
[API] add watched state endpoints
This commit is contained in:
parent
a2e2fd1b89
commit
6ed2308f99
@ -41,6 +41,7 @@ Note:
|
||||
- [Refresh](#refresh-view)
|
||||
- [Cookie](#cookie-view)
|
||||
- [Search](#search-view)
|
||||
- [Watched](#watched-view)
|
||||
- [Ping](#ping-view)
|
||||
|
||||
## Authentication
|
||||
@ -404,6 +405,19 @@ GET /api/search/?query=\<query>
|
||||
|
||||
Returns search results from your query.
|
||||
|
||||
## Watched View
|
||||
POST /api/watched/
|
||||
|
||||
Change watched state, where the `id` can be a single video, or channel/playlist to change all videos belonging to that channel/playlist.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "xxxxxxx",
|
||||
"is_watched": True
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Ping View
|
||||
Validate your connection with the API
|
||||
GET /api/ping/
|
||||
|
@ -23,6 +23,7 @@ from api.views import (
|
||||
VideoProgressView,
|
||||
VideoSimilarView,
|
||||
VideoSponsorView,
|
||||
WatchedView,
|
||||
)
|
||||
from django.urls import path
|
||||
|
||||
@ -124,6 +125,11 @@ urlpatterns = [
|
||||
CookieView.as_view(),
|
||||
name="api-cookie",
|
||||
),
|
||||
path(
|
||||
"watched/",
|
||||
WatchedView.as_view(),
|
||||
name="api-watched",
|
||||
),
|
||||
path(
|
||||
"search/",
|
||||
SearchView.as_view(),
|
||||
|
@ -7,6 +7,7 @@ from home.src.download.yt_dlp_base import CookieHandler
|
||||
from home.src.es.connect import ElasticWrap
|
||||
from home.src.es.snapshot import ElasticSnapshot
|
||||
from home.src.frontend.searching import SearchForm
|
||||
from home.src.frontend.watched import WatchState
|
||||
from home.src.index.channel import YoutubeChannel
|
||||
from home.src.index.generic import Pagination
|
||||
from home.src.index.reindex import ReindexProgress
|
||||
@ -703,6 +704,24 @@ class CookieView(ApiBaseView):
|
||||
return Response(message)
|
||||
|
||||
|
||||
class WatchedView(ApiBaseView):
|
||||
"""resolves to /api/watched/
|
||||
POST: change watched state of video, channel or playlist
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
"""change watched state"""
|
||||
youtube_id = request.data.get("id")
|
||||
is_watched = request.data.get("is_watched")
|
||||
|
||||
if not youtube_id or is_watched is None:
|
||||
message = {"message": "missing id or is_watched"}
|
||||
return Response(message, status=400)
|
||||
|
||||
WatchState(youtube_id, is_watched).change()
|
||||
return Response({"message": "success"}, status=200)
|
||||
|
||||
|
||||
class SearchView(ApiBaseView):
|
||||
"""resolves to /api/search/
|
||||
GET: run a search with the string in the ?query parameter
|
||||
|
@ -74,12 +74,12 @@ class PostData:
|
||||
|
||||
def _watched(self):
|
||||
"""mark as watched"""
|
||||
WatchState(self.exec_val).mark_as_watched()
|
||||
WatchState(self.exec_val, is_watched=True).change()
|
||||
return {"success": True}
|
||||
|
||||
def _un_watched(self):
|
||||
"""mark as unwatched"""
|
||||
WatchState(self.exec_val).mark_as_unwatched()
|
||||
WatchState(self.exec_val, is_watched=False).change()
|
||||
return {"success": True}
|
||||
|
||||
def _change_view(self):
|
||||
|
@ -12,95 +12,94 @@ from home.src.ta.helper import UrlListParser
|
||||
class WatchState:
|
||||
"""handle watched checkbox for videos and channels"""
|
||||
|
||||
def __init__(self, youtube_id):
|
||||
def __init__(self, youtube_id, is_watched):
|
||||
self.youtube_id = youtube_id
|
||||
self.is_watched = is_watched
|
||||
self.stamp = int(datetime.now().timestamp())
|
||||
self.pipeline = f"_ingest/pipeline/watch_{youtube_id}"
|
||||
|
||||
def mark_as_watched(self):
|
||||
"""update es with new watched value"""
|
||||
url_type = self.dedect_type()
|
||||
def change(self):
|
||||
"""change watched state of item(s)"""
|
||||
url_type = self._dedect_type()
|
||||
if url_type == "video":
|
||||
self.mark_vid_watched()
|
||||
elif url_type == "channel":
|
||||
self.mark_channel_watched()
|
||||
elif url_type == "playlist":
|
||||
self.mark_playlist_watched()
|
||||
self.change_vid_state()
|
||||
return
|
||||
|
||||
print(f"{self.youtube_id}: marked as watched")
|
||||
self._add_pipeline()
|
||||
path = f"ta_video/_update_by_query?pipeline=watch_{self.youtube_id}"
|
||||
data = self._build_update_data(url_type)
|
||||
_, _ = ElasticWrap(path).post(data)
|
||||
self._delete_pipeline()
|
||||
|
||||
def mark_as_unwatched(self):
|
||||
"""revert watched state to false"""
|
||||
url_type = self.dedect_type()
|
||||
if url_type == "video":
|
||||
self.mark_vid_watched(revert=True)
|
||||
|
||||
print(f"{self.youtube_id}: revert as unwatched")
|
||||
|
||||
def dedect_type(self):
|
||||
def _dedect_type(self):
|
||||
"""find youtube id type"""
|
||||
print(self.youtube_id)
|
||||
url_process = UrlListParser(self.youtube_id).process_list()
|
||||
url_type = url_process[0]["type"]
|
||||
return url_type
|
||||
|
||||
def mark_vid_watched(self, revert=False):
|
||||
"""change watched status of single video"""
|
||||
def change_vid_state(self):
|
||||
"""change watched state of video"""
|
||||
path = f"ta_video/_update/{self.youtube_id}"
|
||||
data = {
|
||||
"doc": {"player": {"watched": True, "watched_date": self.stamp}}
|
||||
"doc": {
|
||||
"player": {
|
||||
"watched": self.is_watched,
|
||||
"watched_date": self.stamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
if revert:
|
||||
data["doc"]["player"]["watched"] = False
|
||||
|
||||
response, status_code = ElasticWrap(path).post(data=data)
|
||||
if status_code != 200:
|
||||
print(response)
|
||||
raise ValueError("failed to mark video as watched")
|
||||
|
||||
def _get_source(self):
|
||||
"""build source line for update_by_query script"""
|
||||
source = [
|
||||
"ctx._source.player['watched'] = true",
|
||||
f"ctx._source.player['watched_date'] = {self.stamp}",
|
||||
]
|
||||
return "; ".join(source)
|
||||
def _build_update_data(self, url_type):
|
||||
"""build update by query data based on url_type"""
|
||||
term_key_map = {
|
||||
"channel": "channel.channel_id",
|
||||
"playlist": "playlist.keyword",
|
||||
}
|
||||
term_key = term_key_map.get(url_type)
|
||||
|
||||
def mark_channel_watched(self):
|
||||
"""change watched status of every video in channel"""
|
||||
path = "ta_video/_update_by_query"
|
||||
must_list = [
|
||||
{"term": {"channel.channel_id": {"value": self.youtube_id}}},
|
||||
{"term": {"player.watched": {"value": False}}},
|
||||
]
|
||||
data = {
|
||||
"query": {"bool": {"must": must_list}},
|
||||
"script": {
|
||||
"source": self._get_source(),
|
||||
"lang": "painless",
|
||||
return {
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"term": {term_key: {"value": self.youtube_id}}},
|
||||
{
|
||||
"term": {
|
||||
"player.watched": {
|
||||
"value": not self.is_watched
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response, status_code = ElasticWrap(path).post(data=data)
|
||||
if status_code != 200:
|
||||
print(response)
|
||||
raise ValueError("failed mark channel as watched")
|
||||
|
||||
def mark_playlist_watched(self):
|
||||
"""change watched state of all videos in playlist"""
|
||||
path = "ta_video/_update_by_query"
|
||||
must_list = [
|
||||
{"term": {"playlist.keyword": {"value": self.youtube_id}}},
|
||||
{"term": {"player.watched": {"value": False}}},
|
||||
]
|
||||
def _add_pipeline(self):
|
||||
"""add ingest pipeline"""
|
||||
data = {
|
||||
"query": {"bool": {"must": must_list}},
|
||||
"script": {
|
||||
"source": self._get_source(),
|
||||
"lang": "painless",
|
||||
},
|
||||
"description": f"{self.youtube_id}: watched {self.is_watched}",
|
||||
"processors": [
|
||||
{
|
||||
"set": {
|
||||
"field": "player.watched",
|
||||
"value": self.is_watched,
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"field": "player.watched_date",
|
||||
"value": self.stamp,
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
_, _ = ElasticWrap(self.pipeline).put(data)
|
||||
|
||||
response, status_code = ElasticWrap(path).post(data=data)
|
||||
if status_code != 200:
|
||||
print(response)
|
||||
raise ValueError("failed mark playlist as watched")
|
||||
def _delete_pipeline(self):
|
||||
"""delete pipeline"""
|
||||
ElasticWrap(self.pipeline).delete()
|
||||
|
@ -24,14 +24,13 @@ function updateVideoWatchStatus(input1, videoCurrentWatchStatus) {
|
||||
removeProgressBar(videoId);
|
||||
|
||||
let watchStatusIndicator, payload;
|
||||
let apiEndpoint = '/api/watched/'
|
||||
if (videoCurrentWatchStatus === 'watched') {
|
||||
watchStatusIndicator = createWatchStatusIndicator(videoId, 'unwatched');
|
||||
payload = JSON.stringify({ un_watched: videoId });
|
||||
sendPost(payload);
|
||||
apiRequest(apiEndpoint, 'POST', {id: videoId, "is_watched": false})
|
||||
} else if (videoCurrentWatchStatus === 'unwatched') {
|
||||
watchStatusIndicator = createWatchStatusIndicator(videoId, 'watched');
|
||||
payload = JSON.stringify({ watched: videoId });
|
||||
sendPost(payload);
|
||||
apiRequest(apiEndpoint, 'POST', {id: videoId, "is_watched": true})
|
||||
}
|
||||
|
||||
let watchButtons = document.getElementsByClassName('watch-button');
|
||||
@ -76,9 +75,10 @@ function removeProgressBar(videoId) {
|
||||
|
||||
function isWatchedButton(button) {
|
||||
let youtube_id = button.getAttribute('data-id');
|
||||
let payload = JSON.stringify({ watched: youtube_id });
|
||||
let apiEndpoint = '/api/watched/';
|
||||
let data = {id: youtube_id, is_watched: true}
|
||||
button.remove();
|
||||
sendPost(payload);
|
||||
apiRequest(apiEndpoint, 'POST', data);
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
|
Loading…
Reference in New Issue
Block a user