mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-05 00:09:18 +00:00
add playlist sort order toggle, #171
This commit is contained in:
@@ -506,6 +506,9 @@
|
|||||||
"type": "date",
|
"type": "date",
|
||||||
"format": "epoch_second"
|
"format": "epoch_second"
|
||||||
},
|
},
|
||||||
|
"playlist_sort_order": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
"playlist_entries": {
|
"playlist_entries": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"downloaded": {
|
"downloaded": {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class Command(BaseCommand):
|
|||||||
self._mig_channel_tags()
|
self._mig_channel_tags()
|
||||||
self._mig_video_channel_tags()
|
self._mig_video_channel_tags()
|
||||||
self._mig_fix_download_channel_indexed()
|
self._mig_fix_download_channel_indexed()
|
||||||
|
self._mig_add_default_playlist_sort()
|
||||||
|
|
||||||
def _make_folders(self):
|
def _make_folders(self):
|
||||||
"""make expected cache folders"""
|
"""make expected cache folders"""
|
||||||
@@ -389,3 +390,37 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(response)
|
self.stdout.write(response)
|
||||||
sleep(60)
|
sleep(60)
|
||||||
raise CommandError(message)
|
raise CommandError(message)
|
||||||
|
|
||||||
|
def _mig_add_default_playlist_sort(self) -> None:
|
||||||
|
"""migrate from 0.5.4 to 0.5.5 set default playlist sortorder"""
|
||||||
|
self.stdout.write("[MIGRATION] set default playlist sort order")
|
||||||
|
path = "ta_playlist/_update_by_query"
|
||||||
|
data = {
|
||||||
|
"query": {
|
||||||
|
"bool": {
|
||||||
|
"must_not": [{"exists": {"field": "playlist_sort_order"}}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"script": {
|
||||||
|
"source": "ctx._source.playlist_sort_order = 'top'",
|
||||||
|
"lang": "painless",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
response, status_code = ElasticWrap(path).post(data)
|
||||||
|
if status_code in [200, 201]:
|
||||||
|
updated = response.get("updated")
|
||||||
|
if updated:
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.SUCCESS(f" ✓ updated {updated} playlists")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.SUCCESS(" no playlists need updating")
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
message = " 🗙 failed to set default playlist sort order"
|
||||||
|
self.stdout.write(self.style.ERROR(message))
|
||||||
|
self.stdout.write(response)
|
||||||
|
sleep(60)
|
||||||
|
raise CommandError(message)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class PlaylistSerializer(serializers.Serializer):
|
|||||||
playlist_last_refresh = serializers.CharField()
|
playlist_last_refresh = serializers.CharField()
|
||||||
playlist_name = serializers.CharField()
|
playlist_name = serializers.CharField()
|
||||||
playlist_subscribed = serializers.BooleanField()
|
playlist_subscribed = serializers.BooleanField()
|
||||||
|
playlist_sort_order = serializers.ChoiceField(choices=["top", "bottom"])
|
||||||
playlist_thumbnail = serializers.CharField()
|
playlist_thumbnail = serializers.CharField()
|
||||||
playlist_type = serializers.ChoiceField(choices=["regular", "custom"])
|
playlist_type = serializers.ChoiceField(choices=["regular", "custom"])
|
||||||
_index = serializers.CharField(required=False)
|
_index = serializers.CharField(required=False)
|
||||||
@@ -68,7 +69,10 @@ class PlaylistBulkAddSerializer(serializers.Serializer):
|
|||||||
class PlaylistSingleUpdate(serializers.Serializer):
|
class PlaylistSingleUpdate(serializers.Serializer):
|
||||||
"""update state of single playlist"""
|
"""update state of single playlist"""
|
||||||
|
|
||||||
playlist_subscribed = serializers.BooleanField()
|
playlist_subscribed = serializers.BooleanField(required=False)
|
||||||
|
playlist_sort_order = serializers.ChoiceField(
|
||||||
|
choices=["top", "bottom"], required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlaylistListCustomPostSerializer(serializers.Serializer):
|
class PlaylistListCustomPostSerializer(serializers.Serializer):
|
||||||
|
|||||||
@@ -35,11 +35,17 @@ class YoutubePlaylist(YouTubeItem):
|
|||||||
self.get_from_es()
|
self.get_from_es()
|
||||||
if self.json_data:
|
if self.json_data:
|
||||||
subscribed = self.json_data.get("playlist_subscribed")
|
subscribed = self.json_data.get("playlist_subscribed")
|
||||||
|
playlist_sort_order = self.json_data.get("playlist_sort_order")
|
||||||
else:
|
else:
|
||||||
subscribed = False
|
subscribed = False
|
||||||
|
playlist_sort_order = "top"
|
||||||
|
|
||||||
|
playlist_items = "::1" if playlist_sort_order == "top" else "::-1"
|
||||||
|
|
||||||
if scrape or not self.json_data:
|
if scrape or not self.json_data:
|
||||||
self.get_from_youtube()
|
self.get_from_youtube(
|
||||||
|
obs_overwrite={"playlist_items": playlist_items}
|
||||||
|
)
|
||||||
if not self.youtube_meta:
|
if not self.youtube_meta:
|
||||||
self.json_data = False
|
self.json_data = False
|
||||||
return
|
return
|
||||||
@@ -48,8 +54,13 @@ class YoutubePlaylist(YouTubeItem):
|
|||||||
self._ensure_channel()
|
self._ensure_channel()
|
||||||
ids_found = self.get_local_vids()
|
ids_found = self.get_local_vids()
|
||||||
self.get_entries(ids_found)
|
self.get_entries(ids_found)
|
||||||
self.json_data["playlist_entries"] = self.all_members
|
self.json_data.update(
|
||||||
self.json_data["playlist_subscribed"] = subscribed
|
{
|
||||||
|
"playlist_entries": self.all_members,
|
||||||
|
"playlist_subscribed": subscribed,
|
||||||
|
"playlist_sort_order": playlist_sort_order,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def process_youtube_meta(self):
|
def process_youtube_meta(self):
|
||||||
"""extract relevant fields from youtube"""
|
"""extract relevant fields from youtube"""
|
||||||
@@ -190,6 +201,15 @@ class YoutubePlaylist(YouTubeItem):
|
|||||||
self.get_playlist_art()
|
self.get_playlist_art()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def change_sort_order(self, new_sort_order):
|
||||||
|
"""update sort order of playlist"""
|
||||||
|
playlist = YoutubePlaylist(self.youtube_id)
|
||||||
|
playlist.build_json()
|
||||||
|
playlist.json_data["playlist_sort_order"] = new_sort_order
|
||||||
|
playlist.upload_to_es()
|
||||||
|
|
||||||
|
return playlist.json_data
|
||||||
|
|
||||||
def build_nav(self, youtube_id):
|
def build_nav(self, youtube_id):
|
||||||
"""find next and previous in playlist of a given youtube_id"""
|
"""find next and previous in playlist of a given youtube_id"""
|
||||||
cache_root = EnvironmentSettings().get_cache_root()
|
cache_root = EnvironmentSettings().get_cache_root()
|
||||||
|
|||||||
@@ -240,9 +240,24 @@ class PlaylistApiView(ApiBaseView):
|
|||||||
error = ErrorResponseSerializer({"error": "playlist not found"})
|
error = ErrorResponseSerializer({"error": "playlist not found"})
|
||||||
return Response(error.data, status=404)
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
subscribed = validated_data["playlist_subscribed"]
|
subscribed = validated_data.get("playlist_subscribed")
|
||||||
playlist_sub = PlaylistSubscription()
|
sort_order = validated_data.get("playlist_sort_order")
|
||||||
json_data = playlist_sub.change_subscribe(playlist_id, subscribed)
|
|
||||||
|
json_data = None
|
||||||
|
if subscribed is not None:
|
||||||
|
playlist_sub = PlaylistSubscription()
|
||||||
|
json_data = playlist_sub.change_subscribe(playlist_id, subscribed)
|
||||||
|
|
||||||
|
if sort_order:
|
||||||
|
json_data = YoutubePlaylist(playlist_id).change_sort_order(
|
||||||
|
new_sort_order=sort_order
|
||||||
|
)
|
||||||
|
|
||||||
|
if not json_data:
|
||||||
|
error = ErrorResponseSerializer(
|
||||||
|
{"error": "expect playlist_subscribed or playlist_sort_order"}
|
||||||
|
)
|
||||||
|
return Response(error.data, status=400)
|
||||||
|
|
||||||
response_serializer = PlaylistSerializer(json_data)
|
response_serializer = PlaylistSerializer(json_data)
|
||||||
return Response(response_serializer.data)
|
return Response(response_serializer.data)
|
||||||
|
|||||||
10
frontend/src/api/actions/updatePlaylistSortOrder.ts
Normal file
10
frontend/src/api/actions/updatePlaylistSortOrder.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import APIClient from '../../functions/APIClient';
|
||||||
|
|
||||||
|
const updatePlaylistSortOrder = async (playlistId: string, newSortOrder: 'top' | 'bottom') => {
|
||||||
|
return APIClient(`/api/playlist/${playlistId}/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: { playlist_sort_order: newSortOrder },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updatePlaylistSortOrder;
|
||||||
@@ -14,6 +14,7 @@ export type PlaylistType = {
|
|||||||
playlist_channel_id: string;
|
playlist_channel_id: string;
|
||||||
playlist_description: string;
|
playlist_description: string;
|
||||||
playlist_entries: PlaylistEntryType[];
|
playlist_entries: PlaylistEntryType[];
|
||||||
|
playlist_sort_order: 'top' | 'bottom';
|
||||||
playlist_id: string;
|
playlist_id: string;
|
||||||
playlist_last_refresh: string;
|
playlist_last_refresh: string;
|
||||||
playlist_name: string;
|
playlist_name: string;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import useIsAdmin from '../functions/useIsAdmin';
|
|||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
import { ApiResponseType } from '../functions/APIClient';
|
import { ApiResponseType } from '../functions/APIClient';
|
||||||
import NotFound from './NotFound';
|
import NotFound from './NotFound';
|
||||||
|
import updatePlaylistSortOrder from '../api/actions/updatePlaylistSortOrder';
|
||||||
|
|
||||||
export type VideoResponseType = {
|
export type VideoResponseType = {
|
||||||
data?: VideoType[];
|
data?: VideoType[];
|
||||||
@@ -287,6 +288,31 @@ const Playlist = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="toggle">
|
||||||
|
<span>Switch sort order:</span>
|
||||||
|
<div className="toggleBox">
|
||||||
|
<input
|
||||||
|
id="playlist_sort_order"
|
||||||
|
type="checkbox"
|
||||||
|
checked={playlist.playlist_sort_order === 'bottom'}
|
||||||
|
onChange={async () => {
|
||||||
|
const newSortOrder =
|
||||||
|
playlist.playlist_sort_order === 'top' ? 'bottom' : 'top';
|
||||||
|
await updatePlaylistSortOrder(playlist.playlist_id, newSortOrder);
|
||||||
|
setRefresh(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{playlist.playlist_sort_order === 'bottom' ? (
|
||||||
|
<label htmlFor="" className="onbtn">
|
||||||
|
On
|
||||||
|
</label>
|
||||||
|
) : (
|
||||||
|
<label htmlFor="" className="ofbtn">
|
||||||
|
Off
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user