commit
8a852bcd24
7 changed files with 126 additions and 81 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -49,6 +49,7 @@
|
||||||
"daterange",
|
"daterange",
|
||||||
"defusedxml",
|
"defusedxml",
|
||||||
"delenv",
|
"delenv",
|
||||||
|
"denoland",
|
||||||
"dlfields",
|
"dlfields",
|
||||||
"dotdot",
|
"dotdot",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
|
@ -100,6 +101,7 @@
|
||||||
"mccabe",
|
"mccabe",
|
||||||
"mebibytes",
|
"mebibytes",
|
||||||
"MEIPASS",
|
"MEIPASS",
|
||||||
|
"merch",
|
||||||
"Microformat",
|
"Microformat",
|
||||||
"microformats",
|
"microformats",
|
||||||
"mkvtoolsnix",
|
"mkvtoolsnix",
|
||||||
|
|
@ -153,6 +155,7 @@
|
||||||
"threadsafe",
|
"threadsafe",
|
||||||
"tiktok",
|
"tiktok",
|
||||||
"timespec",
|
"timespec",
|
||||||
|
"tinyurl",
|
||||||
"tmpfilename",
|
"tmpfilename",
|
||||||
"toplevel",
|
"toplevel",
|
||||||
"ungroup",
|
"ungroup",
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,13 @@ COPY --chown=app:app --from=python_builder /opt/python /opt/python
|
||||||
COPY --from=ghcr.io/arabcoders/alpine-mp4box /usr/bin/mp4box /usr/bin/mp4box
|
COPY --from=ghcr.io/arabcoders/alpine-mp4box /usr/bin/mp4box /usr/bin/mp4box
|
||||||
COPY --from=ghcr.io/arabcoders/jellyfin-ffmpeg /usr/bin/ffmpeg /usr/bin/ffmpeg
|
COPY --from=ghcr.io/arabcoders/jellyfin-ffmpeg /usr/bin/ffmpeg /usr/bin/ffmpeg
|
||||||
COPY --from=ghcr.io/arabcoders/jellyfin-ffmpeg /usr/bin/ffprobe /usr/bin/ffprobe
|
COPY --from=ghcr.io/arabcoders/jellyfin-ffmpeg /usr/bin/ffprobe /usr/bin/ffprobe
|
||||||
|
COPY --from=denoland/deno:latest /usr/bin/deno /usr/bin/deno
|
||||||
COPY --chown=app:app ./healthcheck.sh /usr/local/bin/healthcheck
|
COPY --chown=app:app ./healthcheck.sh /usr/local/bin/healthcheck
|
||||||
|
|
||||||
ENV PATH="/opt/python/bin:$PATH"
|
ENV PATH="/opt/python/bin:$PATH"
|
||||||
|
|
||||||
RUN chown -R app:app /config /downloads && \
|
RUN chown -R app:app /config /downloads && \
|
||||||
chmod +x /usr/local/bin/healthcheck /usr/bin/mp4box /usr/bin/ffmpeg /usr/bin/ffprobe
|
chmod +x /usr/local/bin/healthcheck /usr/bin/mp4box /usr/bin/ffmpeg /usr/bin/ffprobe /usr/bin/deno
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
VOLUME /downloads
|
VOLUME /downloads
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,24 @@ DEFAULT_PRESETS: list[dict[int, dict[str, str | bool]]] = [
|
||||||
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f60",
|
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f60",
|
||||||
"name": "Info Reader Plugin",
|
"name": "Info Reader Plugin",
|
||||||
"description": 'This preset generate specific filename format and metadata to work with yt-dlp info reader plugins for jellyfin/emby and plex and to make play state sync work for WatchState.\n\nThere is one more step you need to do via Other > Terminal if you have it enabled or directly from container shell\n\nyt-dlp -I0 --write-info-json --write-thumbnail --convert-thumbnails jpg --paths /downloads/youtube -o "%(channel|Unknown_title)s [%(channel_id|Unknown_id)s]/%(title).180B [%(channel_id|Unknown_id)s].%(ext)s" -- https://www.youtube.com/channel/UCClfFsWcT3N2I7VTXXyt84A\n\nChange the url to the channel you want to download.\n\nFor more information please visit \nhttps://github.com/arabcoders/watchstate/blob/master/FAQ.md#how-to-get-watchstate-working-with-youtube-contentlibrary',
|
"description": 'This preset generate specific filename format and metadata to work with yt-dlp info reader plugins for jellyfin/emby and plex and to make play state sync work for WatchState.\n\nThere is one more step you need to do via Other > Terminal if you have it enabled or directly from container shell\n\nyt-dlp -I0 --write-info-json --write-thumbnail --convert-thumbnails jpg --paths /downloads/youtube -o "%(channel|Unknown_title)s [%(channel_id|Unknown_id)s]/%(title).180B [%(channel_id|Unknown_id)s].%(ext)s" -- https://www.youtube.com/channel/UCClfFsWcT3N2I7VTXXyt84A\n\nChange the url to the channel you want to download.\n\nFor more information please visit \nhttps://github.com/arabcoders/watchstate/blob/master/FAQ.md#how-to-get-watchstate-working-with-youtube-contentlibrary',
|
||||||
"template": "%(channel)s [%(channel_id|Unknown_id)s]/Season %(release_date>%Y,upload_date>%Y|Unknown)s/%(release_date>%Y%m%d,upload_date>%Y%m%d)s - %(title).180B [%(extractor)s-%(id)s].%(ext)s",
|
"template": "%(channel)s [%(channel_id|Unknown_id)s]/Season %(release_date>%Y,upload_date>%Y|Unknown)s/%(release_date>%Y%m%d,upload_date>%Y%m%d)s - %(title).180B [%(id)s].%(ext)s",
|
||||||
"cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--windows-filenames --write-info-json --embed-info-json \n--convert-thumbnails jpg --write-thumbnail --embed-metadata",
|
"cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--windows-filenames --write-info-json --embed-info-json \n--convert-thumbnails jpg --write-thumbnail --embed-metadata",
|
||||||
"default": True,
|
"default": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f61",
|
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f61",
|
||||||
"name": "NFO Maker TV",
|
"name": "NFO Maker TV",
|
||||||
"description": "This preset generate specific filename format and metadata to work with media servers like jellyfin/emby and plex.",
|
"description": "This preset generate specific filename format and metadata to work with media servers like jellyfin/emby and plex for episodes.",
|
||||||
"template": "%(channel)s [%(channel_id|Unknown_id)s]/Season %(release_date>%Y,upload_date>%Y|Unknown)s/S%(release_date>%Y,upload_date>%Y)sE%(release_date>%m%d,upload_date>%m%d)s - %(title).100B [%(extractor)s-%(id)s].%(ext)s",
|
"template": "%(channel)s [%(channel_id|Unknown_id)s]/Season %(release_date>%Y,upload_date>%Y|Unknown)s/S%(release_date>%Y,upload_date>%Y)sE%(release_date>%m%d,upload_date>%m%d)s - %(title).100B [%(id)s].%(ext)s",
|
||||||
"cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log \n--windows-filenames --convert-thumbnails jpg --write-thumbnail \n--use-postprocessor NFOMakerTvPP",
|
"cli": '--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--use-postprocessor "NFOMakerPP:when=after_move" \n--windows-filenames --convert-thumbnails jpg --write-thumbnail',
|
||||||
|
"default": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f61",
|
||||||
|
"name": "NFO Maker Movie",
|
||||||
|
"description": "This preset generate specific filename format and metadata to work with media servers like jellyfin/emby and plex for movies.",
|
||||||
|
"template": "%(channel)s [%(channel_id|Unknown_id)s]/Season %(release_date>%Y,upload_date>%Y|Unknown)s/S%(release_date>%Y,upload_date>%Y)sE%(release_date>%m%d,upload_date>%m%d)s - %(title).100B [%(id)s].%(ext)s",
|
||||||
|
"cli": '--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--use-postprocessor "NFOMakerPP:when=after_move;mode=movie" \n--windows-filenames --convert-thumbnails jpg --write-thumbnail',
|
||||||
"default": True,
|
"default": True,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,10 @@ class YoutubeHandler(BaseHandler):
|
||||||
None if the URL is neither.
|
None if the URL is neither.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if m := YoutubeHandler.CHANNEL_REGEX.match(url):
|
if (m := YoutubeHandler.CHANNEL_REGEX.match(url)) and m.group("id"):
|
||||||
return {"type": "channel_id", "id": m.group("id")}
|
return {"type": "channel_id", "id": m.group("id")}
|
||||||
|
|
||||||
if m := YoutubeHandler.PLAYLIST_REGEX.match(url):
|
if (m := YoutubeHandler.PLAYLIST_REGEX.match(url)) and m.group("id"):
|
||||||
return {"type": "playlist_id", "id": m.group("id")}
|
return {"type": "playlist_id", "id": m.group("id")}
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
from yt_dlp.globals import postprocessors
|
from yt_dlp.globals import postprocessors
|
||||||
|
|
||||||
from .make_nfo import NFOMakerMoviePP, NFOMakerTvPP
|
from .nfo_maker import NFOMakerPP
|
||||||
|
|
||||||
_ytptube_pps = {name: value for name, value in globals().items() if name.endswith("PP")}
|
_ytptube_pps = {name: value for name, value in globals().items() if name.endswith("PP")}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
|
|
||||||
|
|
||||||
class NFOMaker(PostProcessor):
|
class NFOMakerPP(PostProcessor):
|
||||||
"""
|
"""
|
||||||
A Post-Processor that writes metadata to an NFO file using a simple
|
A Post-Processor that writes metadata to an NFO file using a simple
|
||||||
placeholder-based template engine.
|
placeholder-based template engine.
|
||||||
|
|
@ -24,8 +24,6 @@ class NFOMaker(PostProcessor):
|
||||||
- ("date_field", "%Y") -> year from date_field
|
- ("date_field", "%Y") -> year from date_field
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_MAPPING: dict[str, Any] = {}
|
|
||||||
_TEMPLATE: str | None = None
|
|
||||||
_DATE_FIELDS: tuple[str, ...] = ("upload_date", "release_date", "aired", "premiered")
|
_DATE_FIELDS: tuple[str, ...] = ("upload_date", "release_date", "aired", "premiered")
|
||||||
|
|
||||||
_URL_PAT = re.compile(
|
_URL_PAT = re.compile(
|
||||||
|
|
@ -43,6 +41,79 @@ class NFOMaker(PostProcessor):
|
||||||
r"snapchat|reddit|telegram|t\.me|whatsapp|link\s+in\s+bio|bit\.ly|tinyurl|goo\.gl"
|
r"snapchat|reddit|telegram|t\.me|whatsapp|link\s+in\s+bio|bit\.ly|tinyurl|goo\.gl"
|
||||||
r")\b"
|
r")\b"
|
||||||
)
|
)
|
||||||
|
_args: dict[str, Any] = {}
|
||||||
|
_MODE: dict = {
|
||||||
|
"tv": {
|
||||||
|
"mapping": {
|
||||||
|
"title": "title",
|
||||||
|
"season": ("season_number", "season", "year", ("release_date", "%Y"), ("upload_date", "%Y")),
|
||||||
|
"episode": (
|
||||||
|
"episode_number",
|
||||||
|
"episode",
|
||||||
|
("release_date", "%m%d"),
|
||||||
|
("upload_date", "%m%d"),
|
||||||
|
"unique_id",
|
||||||
|
),
|
||||||
|
"aired": ("release_date", "upload_date"),
|
||||||
|
"author": "uploader",
|
||||||
|
"plot": "description",
|
||||||
|
"id": "id",
|
||||||
|
"extractor": "extractor",
|
||||||
|
},
|
||||||
|
"template": """
|
||||||
|
<episodedetails>
|
||||||
|
<title>{title}</title>
|
||||||
|
<season>{season}</season>
|
||||||
|
<episode>{episode}</episode>
|
||||||
|
<aired>{aired}</aired>
|
||||||
|
<uniqueid type="cmdb">{unique_id}</uniqueid>
|
||||||
|
<uniqueid type="{extractor}">{id}</uniqueid>
|
||||||
|
<plot>{plot}</plot>
|
||||||
|
</episodedetails>
|
||||||
|
""".strip("\n"),
|
||||||
|
},
|
||||||
|
"movie": {
|
||||||
|
"mapping": {
|
||||||
|
"title": "title",
|
||||||
|
"plot": "description",
|
||||||
|
"runtime": "duration",
|
||||||
|
"thumb": "thumbnail",
|
||||||
|
"id": "id",
|
||||||
|
"country": "language",
|
||||||
|
"aired": ("release_date", "upload_date"),
|
||||||
|
"extractor": "extractor",
|
||||||
|
"year": ("release_year", "year"),
|
||||||
|
"author": "uploader",
|
||||||
|
"trailer": "webpage_url",
|
||||||
|
},
|
||||||
|
"template": """
|
||||||
|
<movie>
|
||||||
|
<title>{title}</title>
|
||||||
|
<plot>{plot}</plot>
|
||||||
|
<runtime>{runtime}</runtime>
|
||||||
|
<thumb aspect="poster" preview="{thumb}">{thumb}</thumb>
|
||||||
|
<id>{id}</id>
|
||||||
|
<uniqueid type="cmdb">{unique_id}</uniqueid>
|
||||||
|
<uniqueid type="{extractor}" default="true">{id}</uniqueid>
|
||||||
|
<country>{country}</country>
|
||||||
|
<premiered>{aired}</premiered>
|
||||||
|
<year>{year}</year>
|
||||||
|
<trailer>{trailer}</trailer>
|
||||||
|
</movie>
|
||||||
|
""".strip("\n"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mode: str = "tv"
|
||||||
|
"The NFO mode to use. Either 'tv' or 'movie'. Default is 'tv'."
|
||||||
|
|
||||||
|
prefix: bool = True
|
||||||
|
"Prefix episodes with 1 for better sorting."
|
||||||
|
|
||||||
|
def __init__(self, downloader, mode: str = "tv", prefix: bool = True) -> None:
|
||||||
|
PostProcessor.__init__(self, downloader)
|
||||||
|
self.mode = str(mode).lower()
|
||||||
|
self.prefix = prefix in (True, "true", "1", 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pp_key(cls) -> str:
|
def pp_key(cls) -> str:
|
||||||
|
|
@ -53,8 +124,8 @@ class NFOMaker(PostProcessor):
|
||||||
self.to_screen("No info provided to NFO Maker.")
|
self.to_screen("No info provided to NFO Maker.")
|
||||||
return [], {}
|
return [], {}
|
||||||
|
|
||||||
if not self._TEMPLATE:
|
if self.mode not in self._MODE:
|
||||||
self.to_screen("NFO template not set, skipping NFO creation.")
|
self.to_screen(f"Invalid mode '{self.mode}'.")
|
||||||
return [], info
|
return [], info
|
||||||
|
|
||||||
# prefer explicit final path if present, else fall back to filename
|
# prefer explicit final path if present, else fall back to filename
|
||||||
|
|
@ -103,7 +174,7 @@ class NFOMaker(PostProcessor):
|
||||||
def _collect_nfo_data(self, info: dict) -> dict[str, Any]:
|
def _collect_nfo_data(self, info: dict) -> dict[str, Any]:
|
||||||
data: dict[str, Any] = {}
|
data: dict[str, Any] = {}
|
||||||
|
|
||||||
for nfo_name, spec in self._MAPPING.items():
|
for nfo_name, spec in self._MODE[self.mode].get("mapping", {}).items():
|
||||||
try:
|
try:
|
||||||
values = spec if isinstance(spec, tuple) else (spec,)
|
values = spec if isinstance(spec, tuple) else (spec,)
|
||||||
resolved_key = None
|
resolved_key = None
|
||||||
|
|
@ -166,7 +237,7 @@ class NFOMaker(PostProcessor):
|
||||||
self.to_screen("Invalid aired date parts, skipping NFO creation.")
|
self.to_screen("Invalid aired date parts, skipping NFO creation.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.to_screen(f"Creating NFO file at {nfo_file!s}")
|
self.to_screen(f"Creating {self.mode} NFO file at {nfo_file!s}")
|
||||||
nfo_file.parent.mkdir(parents=True, exist_ok=True)
|
nfo_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
nfo_file.touch(exist_ok=True)
|
nfo_file.touch(exist_ok=True)
|
||||||
|
|
||||||
|
|
@ -174,7 +245,7 @@ class NFOMaker(PostProcessor):
|
||||||
data = dict(data) # do not mutate original
|
data = dict(data) # do not mutate original
|
||||||
data["unique_id"] = self._build_unique_id(dt, real_file)
|
data["unique_id"] = self._build_unique_id(dt, real_file)
|
||||||
|
|
||||||
self._write(nfo_file=nfo_file, text=self._TEMPLATE or "", repl=data)
|
self._write(nfo_file=nfo_file, text=self._MODE[self.mode].get("template", ""), repl=data)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -230,7 +301,7 @@ class NFOMaker(PostProcessor):
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
if fmt and isinstance(fmt, str):
|
if fmt and isinstance(fmt, str):
|
||||||
date_s = NFOMaker._normalize_date(raw)
|
date_s = NFOMakerPP._normalize_date(raw)
|
||||||
if date_s:
|
if date_s:
|
||||||
try:
|
try:
|
||||||
dt = datetime.now(tz=UTC).strptime(date_s, "%Y-%m-%d")
|
dt = datetime.now(tz=UTC).strptime(date_s, "%Y-%m-%d")
|
||||||
|
|
@ -265,10 +336,18 @@ class NFOMaker(PostProcessor):
|
||||||
for key, value in safe_repl.items():
|
for key, value in safe_repl.items():
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if self.prefix and key in ("episode",):
|
||||||
|
try:
|
||||||
|
value = f"1{value}"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
rendered = rendered.replace(f"{{{key}}}", str(value))
|
rendered = rendered.replace(f"{{{key}}}", str(value))
|
||||||
|
|
||||||
# remove any unresolved placeholder lines
|
# remove any unresolved placeholder lines
|
||||||
unresolved_keys: Iterable[str] = set({*self._MAPPING.keys(), *safe_repl.keys()})
|
mapping = self._MODE[self.mode].get("mapping", {})
|
||||||
|
unresolved_keys: Iterable[str] = set({*mapping, *safe_repl.keys()})
|
||||||
pattern = re.compile(rf".*{{(?:{'|'.join(map(re.escape, unresolved_keys))})}}.*")
|
pattern = re.compile(rf".*{{(?:{'|'.join(map(re.escape, unresolved_keys))})}}.*")
|
||||||
rendered = "\n".join(line for line in rendered.splitlines() if not pattern.fullmatch(line))
|
rendered = "\n".join(line for line in rendered.splitlines() if not pattern.fullmatch(line))
|
||||||
|
|
||||||
|
|
@ -336,58 +415,3 @@ class NFOMaker(PostProcessor):
|
||||||
summary = cut[: dot + 1] if 0 < dot else cut.rstrip()
|
summary = cut[: dot + 1] if 0 < dot else cut.rstrip()
|
||||||
|
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
|
|
||||||
class NFOMakerTvPP(NFOMaker):
|
|
||||||
_MAPPING = {
|
|
||||||
"title": "title",
|
|
||||||
"season": ("season_number", "season", "year", ("release_date", "%Y"), ("upload_date", "%Y")),
|
|
||||||
"episode": ("episode_number", "episode", ("release_date", "%m%d"), ("upload_date", "%m%d"), "unique_id"),
|
|
||||||
"aired": ("release_date", "upload_date"),
|
|
||||||
"author": "uploader",
|
|
||||||
"plot": "description",
|
|
||||||
"id": "id",
|
|
||||||
"extractor": "extractor",
|
|
||||||
}
|
|
||||||
_TEMPLATE = """
|
|
||||||
<episodedetails>
|
|
||||||
<title>{title}</title>
|
|
||||||
<season>{season}</season>
|
|
||||||
<episode>{episode}</episode>
|
|
||||||
<aired>{aired}</aired>
|
|
||||||
<uniqueid type="cmdb">{unique_id}</uniqueid>
|
|
||||||
<uniqueid type="{extractor}">{id}</uniqueid>
|
|
||||||
<plot>{plot}</plot>
|
|
||||||
</episodedetails>
|
|
||||||
""".strip("\n")
|
|
||||||
|
|
||||||
|
|
||||||
class NFOMakerMoviePP(NFOMaker):
|
|
||||||
_MAPPING = {
|
|
||||||
"title": "title",
|
|
||||||
"plot": "description",
|
|
||||||
"runtime": "duration",
|
|
||||||
"thumb": "thumbnail",
|
|
||||||
"id": "id",
|
|
||||||
"country": "language",
|
|
||||||
"aired": ("release_date", "upload_date"),
|
|
||||||
"extractor": "extractor",
|
|
||||||
"year": ("release_year", "year"),
|
|
||||||
"author": "uploader",
|
|
||||||
"trailer": "webpage_url",
|
|
||||||
}
|
|
||||||
_TEMPLATE = """
|
|
||||||
<movie>
|
|
||||||
<title>{title}</title>
|
|
||||||
<plot>{plot}</plot>
|
|
||||||
<runtime>{runtime}</runtime>
|
|
||||||
<thumb aspect="poster" preview="{thumb}">{thumb}</thumb>
|
|
||||||
<id>{id}</id>
|
|
||||||
<uniqueid type="cmdb">{unique_id}</uniqueid>
|
|
||||||
<uniqueid type="{extractor}" default="true">{id}</uniqueid>
|
|
||||||
<country>{country}</country>
|
|
||||||
<premiered>{aired}</premiered>
|
|
||||||
<year>{year}</year>
|
|
||||||
<trailer>{trailer}</trailer>
|
|
||||||
</movie>
|
|
||||||
""".strip("\n")
|
|
||||||
|
|
@ -764,7 +764,7 @@ const removeItem = async (item: StoreItem) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryItem = (item: StoreItem, re_add = false) => {
|
const retryItem = (item: StoreItem, re_add: boolean = false, remove_file: boolean = false) => {
|
||||||
const item_req: Partial<StoreItem> = {
|
const item_req: Partial<StoreItem> = {
|
||||||
url: item.url,
|
url: item.url,
|
||||||
preset: item.preset,
|
preset: item.preset,
|
||||||
|
|
@ -776,7 +776,7 @@ const retryItem = (item: StoreItem, re_add = false) => {
|
||||||
auto_start: item.auto_start,
|
auto_start: item.auto_start,
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit('item_delete', { id: item._id, remove_file: false })
|
socket.emit('item_delete', { id: item._id, remove_file: remove_file })
|
||||||
|
|
||||||
if (selectedElms.value.includes(item._id || '')) {
|
if (selectedElms.value.includes(item._id || '')) {
|
||||||
selectedElms.value = selectedElms.value.filter(i => i !== item._id)
|
selectedElms.value = selectedElms.value.filter(i => i !== item._id)
|
||||||
|
|
@ -860,14 +860,18 @@ const removeFromArchiveDialog = (item: StoreItem) => {
|
||||||
dialog_confirm.value.visible = true
|
dialog_confirm.value.visible = true
|
||||||
dialog_confirm.value.title = 'Remove from Archive'
|
dialog_confirm.value.title = 'Remove from Archive'
|
||||||
dialog_confirm.value.message = `Remove '${item.title || item.id || item.url || '??'}' from archive?`
|
dialog_confirm.value.message = `Remove '${item.title || item.id || item.url || '??'}' from archive?`
|
||||||
dialog_confirm.value.options = [
|
const opts = [
|
||||||
{ key: 'remove_history', label: 'Also, Remove from history.' },
|
{ key: 'remove_history', label: 'Remove from history.' },
|
||||||
{ key: 're_add', label: 'Re-add to download form.' },
|
{ key: 're_add', label: 'Re-add to download form.' },
|
||||||
]
|
];
|
||||||
|
if (config.app.remove_files) {
|
||||||
|
opts.push({ key: 'dont_remove_file', label: "Don't remove associated files." })
|
||||||
|
}
|
||||||
|
dialog_confirm.value.options = opts
|
||||||
dialog_confirm.value.confirm = (opts: any) => removeFromArchive(item, opts)
|
dialog_confirm.value.confirm = (opts: any) => removeFromArchive(item, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeFromArchive = async (item: StoreItem, opts?: { re_add?: boolean, remove_history?: boolean }) => {
|
const removeFromArchive = async (item: StoreItem, opts?: { re_add?: boolean, remove_history?: boolean, dont_remove_file?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
const req = await request(`/api/history/${item._id}/archive`, { method: 'DELETE' })
|
const req = await request(`/api/history/${item._id}/archive`, { method: 'DELETE' })
|
||||||
const data = await req.json()
|
const data = await req.json()
|
||||||
|
|
@ -883,13 +887,18 @@ const removeFromArchive = async (item: StoreItem, opts?: { re_add?: boolean, rem
|
||||||
dialog_confirm.value.visible = false
|
dialog_confirm.value.visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let file_delete = config.app.remove_files
|
||||||
|
if (opts?.dont_remove_file) {
|
||||||
|
file_delete = false
|
||||||
|
}
|
||||||
|
|
||||||
if (opts?.re_add) {
|
if (opts?.re_add) {
|
||||||
retryItem(item, true)
|
retryItem(item, true, file_delete)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts?.remove_history) {
|
if (opts?.remove_history) {
|
||||||
socket.emit('item_delete', { id: item._id, remove_file: false })
|
socket.emit('item_delete', { id: item._id, remove_file: file_delete })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue