This commit is contained in:
NicoBeh 2026-07-06 11:32:27 +08:00 committed by GitHub
commit 5f0ba2929d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 120 additions and 2 deletions

View File

@ -41,6 +41,8 @@ class AppConfigDownloadsSerializer(
limit_speed = serializers.IntegerField(allow_null=True)
sleep_interval = serializers.IntegerField(allow_null=True)
autodelete_days = serializers.IntegerField(allow_null=True)
autodelete_empty_channels = serializers.BooleanField(required=False)
autodelete_empty_playlists = serializers.BooleanField(required=False)
format = serializers.CharField(allow_null=True)
format_sort = serializers.CharField(allow_null=True)
add_metadata = serializers.BooleanField()

View File

@ -32,6 +32,8 @@ class DownloadsConfigType(TypedDict):
limit_speed: int | None
sleep_interval: int | None
autodelete_days: int | None
autodelete_empty_channels: bool
autodelete_empty_playlists: bool
format: str | None
format_sort: str | None
add_metadata: bool
@ -81,6 +83,8 @@ class AppConfig:
"limit_speed": None,
"sleep_interval": 10,
"autodelete_days": None,
"autodelete_empty_channels": False,
"autodelete_empty_playlists": False,
"format": None,
"format_sort": None,
"add_metadata": False,

View File

@ -332,7 +332,9 @@ def get_channels(
def get_playlists(
subscribed_only: bool, source: list[str] | None = None
subscribed_only: bool,
source: list[str] | None = None,
channel_id: str | None = None,
) -> list[dict]:
"""get list of playlists"""
@ -343,8 +345,12 @@ def get_playlists(
must_list = [{"term": {"playlist_active": {"value": True}}}]
if subscribed_only:
must_list.append({"term": {"playlist_subscribed": {"value": True}}})
if channel_id:
must_list.append(
{"term": {"playlist_channel_id": {"value": channel_id}}}
)
data = {"query": {"bool": {"must": must_list}}} # type: ignore
data["query"] = {"bool": {"must": must_list}} # type: ignore
if source:
data["_source"] = source # type: ignore

View File

@ -16,6 +16,7 @@ from common.src.env_settings import EnvironmentSettings
from common.src.es_connect import ElasticWrap, IndexPaginate
from common.src.helper import (
get_channel_overwrites,
get_channels,
get_playlists,
ignore_filelist,
rand_sleep,
@ -280,6 +281,8 @@ class DownloadPostProcess(DownloaderBase):
"""run all functions"""
self.auto_delete_all()
self.auto_delete_overwrites()
self.auto_delete_empty_playlists()
self.auto_delete_empty_channels()
self.refresh_playlist()
self.match_videos()
self.get_comments()
@ -361,6 +364,63 @@ class DownloadPostProcess(DownloaderBase):
PendingList(youtube_ids=parsed_ids).parse_url_list(status="ignore")
def auto_delete_empty_playlists(self):
"""handle auto delete empty playlists"""
if not self.config["downloads"]["autodelete_empty_playlists"]:
return
print("auto delete empty playlists")
playlists = get_playlists(
subscribed_only=False,
source=[
"playlist_id",
"playlist_subscribed",
"playlist_entries",
"playlist_type",
],
)
for playlist in playlists:
if playlist.get("playlist_type") == "custom":
continue
if playlist.get("playlist_subscribed"):
continue
entries = playlist.get("playlist_entries", [])
if any(entry.get("downloaded") for entry in entries):
continue
playlist_id = playlist["playlist_id"]
print(f"{playlist_id}: auto delete empty playlist")
YoutubePlaylist(playlist_id).delete_metadata()
def auto_delete_empty_channels(self):
"""handle auto delete empty channels"""
if not self.config["downloads"]["autodelete_empty_channels"]:
return
print("auto delete empty channels")
channels = get_channels(
subscribed_only=False,
source=["channel_id", "channel_subscribed"],
)
for channel in channels:
if channel.get("channel_subscribed"):
continue
channel_id = channel["channel_id"]
channel_handler = YoutubeChannel(channel_id)
if get_playlists(
subscribed_only=False,
source=["playlist_id"],
channel_id=channel_id,
):
continue
if channel_handler.get_channel_videos():
continue
print(f"{channel_id}: auto delete empty channel")
channel_handler.delete_channel()
def refresh_playlist(self) -> None:
"""match videos with playlists"""
self.add_playlists_to_refresh()

View File

@ -13,6 +13,8 @@ export type AppSettingsConfigType = {
limit_speed: number | null;
sleep_interval: number | null;
autodelete_days: number | null;
autodelete_empty_channels: boolean;
autodelete_empty_playlists: boolean;
format: string | null;
format_sort: string | null;
add_metadata: boolean;

View File

@ -50,6 +50,10 @@ const SettingsApplication = () => {
const [currentThrottledRate, setCurrentThrottledRate] = useState<number | null>(null);
const [currentScrapingSleep, setCurrentScrapingSleep] = useState<number | null>(null);
const [currentAutodelete, setCurrentAutodelete] = useState<number | null>(null);
const [currentAutodeleteEmptyChannels, setCurrentAutodeleteEmptyChannels] =
useState<boolean>(false);
const [currentAutodeleteEmptyPlaylists, setCurrentAutodeleteEmptyPlaylists] =
useState<boolean>(false);
// Download Format
const [downloadsFormat, setDownloadsFormat] = useState<string | null>(null);
@ -106,6 +110,12 @@ const SettingsApplication = () => {
setCurrentThrottledRate(appSettingsConfigData?.downloads.throttledratelimit || null);
setCurrentScrapingSleep(appSettingsConfigData?.downloads.sleep_interval || null);
setCurrentAutodelete(appSettingsConfigData?.downloads.autodelete_days || null);
setCurrentAutodeleteEmptyChannels(
appSettingsConfigData?.downloads.autodelete_empty_channels || false,
);
setCurrentAutodeleteEmptyPlaylists(
appSettingsConfigData?.downloads.autodelete_empty_playlists || false,
);
// Download Format
setDownloadsFormat(appSettingsConfigData?.downloads.format || null);
@ -315,6 +325,18 @@ const SettingsApplication = () => {
<li>Can also be configured on a per channel basis.</li>
</ul>
</li>
<li>
Auto delete empty unsubscribed playlists.
<ul>
<li>The cleanup task triggers after the download finishes.</li>
</ul>
</li>
<li>
Auto delete empty unsubscribed channels.
<ul>
<li>The cleanup task triggers after the download finishes.</li>
</ul>
</li>
</ul>
</div>
)}
@ -372,6 +394,26 @@ const SettingsApplication = () => {
updateCallback={handleUpdateConfig}
/>
</div>
<div className="settings-box-wrapper">
<div>
<p>Auto delete empty playlists</p>
</div>
<ToggleConfig
name="downloads.autodelete_empty_playlists"
value={currentAutodeleteEmptyPlaylists}
updateCallback={handleUpdateConfig}
/>
</div>
<div className="settings-box-wrapper">
<div>
<p>Auto delete empty channels</p>
</div>
<ToggleConfig
name="downloads.autodelete_empty_channels"
value={currentAutodeleteEmptyChannels}
updateCallback={handleUpdateConfig}
/>
</div>
</div>
<div className="info-box-item">
<h2 id="format">Download Format</h2>

View File

@ -20,6 +20,8 @@ export const useAppSettingsStore = create<AppSettingsState>(set => ({
limit_speed: null,
sleep_interval: null,
autodelete_days: null,
autodelete_empty_channels: false,
autodelete_empty_playlists: false,
format: null,
format_sort: null,
add_metadata: false,