Merge pull request #316 from arabcoders/dev
Renamed presets and added optimized preset for mobile devices
This commit is contained in:
commit
8e3e7dda80
9 changed files with 142 additions and 65 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -30,6 +30,7 @@
|
||||||
"edgechromium",
|
"edgechromium",
|
||||||
"engineio",
|
"engineio",
|
||||||
"euuo",
|
"euuo",
|
||||||
|
"faststart",
|
||||||
"finaldir",
|
"finaldir",
|
||||||
"flac",
|
"flac",
|
||||||
"forcejson",
|
"forcejson",
|
||||||
|
|
@ -52,6 +53,7 @@
|
||||||
"Microformat",
|
"Microformat",
|
||||||
"microformats",
|
"microformats",
|
||||||
"mkvtoolsnix",
|
"mkvtoolsnix",
|
||||||
|
"movflags",
|
||||||
"mpegts",
|
"mpegts",
|
||||||
"msvideo",
|
"msvideo",
|
||||||
"muxdelay",
|
"muxdelay",
|
||||||
|
|
@ -73,6 +75,7 @@
|
||||||
"qtpy",
|
"qtpy",
|
||||||
"quicktime",
|
"quicktime",
|
||||||
"rejecttitle",
|
"rejecttitle",
|
||||||
|
"remux",
|
||||||
"rtime",
|
"rtime",
|
||||||
"smhd",
|
"smhd",
|
||||||
"socketio",
|
"socketio",
|
||||||
|
|
|
||||||
|
|
@ -20,34 +20,39 @@ DEFAULT_PRESETS: list[dict[int, dict[str, str | bool]]] = [
|
||||||
"id": "3e163c6c-64eb-4448-924f-814b629b3810",
|
"id": "3e163c6c-64eb-4448-924f-814b629b3810",
|
||||||
"name": "default",
|
"name": "default",
|
||||||
"default": True,
|
"default": True,
|
||||||
|
"description": "Default preset for yt-dlp. It will download whatever yt-dlp decides is the best quality for the video and audio.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5bf9c42b-8852-468a-99f5-915622dfba25",
|
"id": "441675ed-b739-40f0-a0b0-1ecfcb9dc48d",
|
||||||
"name": "Best video and audio",
|
"name": "Mobile",
|
||||||
"cli": "--format 'bv+ba/b'",
|
"cli": '-t mp4 --merge-output-format mp4 --add-chapters --remux-video mp4 \n--embed-metadata --embed-thumbnail \n--postprocessor-args "-movflags +faststart"',
|
||||||
"default": True,
|
"default": True,
|
||||||
|
"description": "This preset is designed for mobile devices. It will download the best quality video and audio in mp4 format, merge them, and add chapters, metadata, and thumbnail.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "441675ed-b739-40f0-a0b0-1ecfcb9dc48b",
|
"id": "441675ed-b739-40f0-a0b0-1ecfcb9dc48b",
|
||||||
"name": "1080p H264/m4a or best available",
|
"name": "1080p",
|
||||||
"cli": "-S vcodec:h264 --format 'bv[height<=1080][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'",
|
"cli": "-S vcodec:h264 --format 'bv[height<=1080][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'",
|
||||||
"default": True,
|
"default": True,
|
||||||
|
"description": "Download the best quality video and audio in mp4 format for 1080p resolution.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9719fcc3-4cf2-4d88-b1e4-74dff3dba00e",
|
"id": "9719fcc3-4cf2-4d88-b1e4-74dff3dba00e",
|
||||||
"name": "720p h264/m4a or best available",
|
"name": "720p",
|
||||||
"cli": "-S vcodec:h264 --format 'bv[height<=720][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'",
|
"cli": "-S vcodec:h264 --format 'bv[height<=720][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'",
|
||||||
"default": True,
|
"default": True,
|
||||||
|
"description": "Download the best quality video and audio in mp4 format for 720p resolution.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "a6fd4b25-2b3e-458d-bb57-b75e41cc4330",
|
"id": "a6fd4b25-2b3e-458d-bb57-b75e41cc4330",
|
||||||
"name": "Audio only",
|
"name": "Audio Only",
|
||||||
"cli": "--extract-audio --add-chapters --embed-metadata --embed-thumbnail --format 'bestaudio/best'",
|
"cli": "--extract-audio --add-chapters --embed-metadata --embed-thumbnail --format 'bestaudio/best'",
|
||||||
"default": True,
|
"default": True,
|
||||||
|
"description": "This preset is designed to download only the audio of the video. It will extract the audio, add chapters, metadata, and thumbnail.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f60",
|
"id": "2ade2c28-cad4-4a06-b7eb-2439fdf46f60",
|
||||||
"name": "yt-dlp 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',
|
||||||
"folder": "youtube",
|
"folder": "youtube",
|
||||||
"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 [%(extractor)s-%(id)s].%(ext)s",
|
||||||
|
|
@ -104,14 +109,16 @@ class Presets(metaclass=Singleton):
|
||||||
_instance = None
|
_instance = None
|
||||||
"""The instance of the class."""
|
"""The instance of the class."""
|
||||||
|
|
||||||
|
_config: Config = None
|
||||||
|
|
||||||
_default: list[Preset] = []
|
_default: list[Preset] = []
|
||||||
|
|
||||||
def __init__(self, file: str | Path | None = None, config: Config | None = None):
|
def __init__(self, file: str | Path | None = None, config: Config | None = None):
|
||||||
Presets._instance = self
|
Presets._instance = self
|
||||||
|
|
||||||
config = config or Config.get_instance()
|
self._config = config or Config.get_instance()
|
||||||
|
|
||||||
self._file: Path = Path(file) if file else Path(config.config_path).joinpath("presets.json")
|
self._file: Path = Path(file) if file else Path(self._config.config_path).joinpath("presets.json")
|
||||||
|
|
||||||
if self._file.exists() and "600" != self._file.stat().st_mode:
|
if self._file.exists() and "600" != self._file.stat().st_mode:
|
||||||
try:
|
try:
|
||||||
|
|
@ -163,6 +170,10 @@ class Presets(metaclass=Singleton):
|
||||||
"""
|
"""
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
|
if not self.get(self._config.default_preset):
|
||||||
|
LOG.error(f"Default preset '{self._config.default_preset}' not found, using 'default' preset.")
|
||||||
|
self._config.default_preset = "default"
|
||||||
|
|
||||||
def get_all(self) -> list[Preset]:
|
def get_all(self) -> list[Preset]:
|
||||||
"""Return the items."""
|
"""Return the items."""
|
||||||
return self._default + self._items
|
return self._default + self._items
|
||||||
|
|
|
||||||
|
|
@ -77,13 +77,14 @@ async def convert(request: Request) -> Response:
|
||||||
|
|
||||||
|
|
||||||
@route("GET", "api/yt-dlp/url/info/", "get_info")
|
@route("GET", "api/yt-dlp/url/info/", "get_info")
|
||||||
async def get_info(request: Request, cache: Cache) -> Response:
|
async def get_info(request: Request, cache: Cache, config: Config) -> Response:
|
||||||
"""
|
"""
|
||||||
Get the video info.
|
Get the video info.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (Request): The request object.
|
request (Request): The request object.
|
||||||
cache (Cache): The cache instance.
|
cache (Cache): The cache instance.
|
||||||
|
config (Config): The config instance.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response: The response object
|
Response: The response object
|
||||||
|
|
@ -91,25 +92,27 @@ async def get_info(request: Request, cache: Cache) -> Response:
|
||||||
"""
|
"""
|
||||||
url: str | None = request.query.get("url")
|
url: str | None = request.query.get("url")
|
||||||
if not url:
|
if not url:
|
||||||
return web.json_response(data={"error": "URL is required."}, status=web.HTTPBadRequest.status_code)
|
return web.json_response(
|
||||||
|
data={"status": False, "message": "URL is required.", "error": "URL is required."},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
validate_url(url)
|
validate_url(url)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return web.json_response(data={"error": str(e)}, status=web.HTTPBadRequest.status_code)
|
return web.json_response(
|
||||||
|
data={"status": False, "message": str(e), "error": str(e)},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
config = Config.get_instance()
|
preset: str = request.query.get("preset", config.default_preset)
|
||||||
|
exists: Preset | None = Presets.get_instance().get(preset)
|
||||||
preset = request.query.get("preset")
|
if not exists:
|
||||||
if preset:
|
msg: str = f"Preset '{preset}' does not exist."
|
||||||
exists: Preset | None = Presets.get_instance().get(preset)
|
return web.json_response(
|
||||||
if not exists:
|
data={"status": False, "message": msg, "error": msg},
|
||||||
return web.json_response(
|
status=web.HTTPBadRequest.status_code,
|
||||||
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
)
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
preset: str = config.default_preset
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
key: str = cache.hash(f"{preset}:{url}")
|
key: str = cache.hash(f"{preset}:{url}")
|
||||||
|
|
@ -118,6 +121,7 @@ async def get_info(request: Request, cache: Cache) -> Response:
|
||||||
data: Any | None = cache.get(key)
|
data: Any | None = cache.get(key)
|
||||||
data["_cached"] = {
|
data["_cached"] = {
|
||||||
"status": "hit",
|
"status": "hit",
|
||||||
|
"preset": preset,
|
||||||
"key": key,
|
"key": key,
|
||||||
"ttl": data.get("_cached", {}).get("ttl", 300),
|
"ttl": data.get("_cached", {}).get("ttl", 300),
|
||||||
"ttl_left": data.get("_cached", {}).get("expires", time.time() + 300) - time.time(),
|
"ttl_left": data.get("_cached", {}).get("expires", time.time() + 300) - time.time(),
|
||||||
|
|
@ -130,7 +134,7 @@ async def get_info(request: Request, cache: Cache) -> Response:
|
||||||
if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None):
|
if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None):
|
||||||
opts["proxy"] = ytdlp_proxy
|
opts["proxy"] = ytdlp_proxy
|
||||||
|
|
||||||
ytdlp_opts = YTDLPOpts.get_instance().preset(name=preset).add(opts).get_all()
|
ytdlp_opts: dict = YTDLPOpts.get_instance().preset(name=preset).add(opts).get_all()
|
||||||
|
|
||||||
data = extract_info(
|
data = extract_info(
|
||||||
config=ytdlp_opts,
|
config=ytdlp_opts,
|
||||||
|
|
@ -153,6 +157,7 @@ async def get_info(request: Request, cache: Cache) -> Response:
|
||||||
|
|
||||||
data["_cached"] = {
|
data["_cached"] = {
|
||||||
"status": "miss",
|
"status": "miss",
|
||||||
|
"preset": preset,
|
||||||
"key": key,
|
"key": key,
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"ttl_left": 300,
|
"ttl_left": 300,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dropdown" :class="{ 'is-active': isOpen, 'drop-up': dropUp }" ref="dropdown">
|
<div class="dropdown" :class="{ 'is-active': isOpen, 'drop-up': dropUp }" ref="dropdown">
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<button class="button is-fullwidth is-justify-content-space-between" aria-haspopup="true"
|
<button class="button is-fullwidth is-justify-content-space-between" aria-haspopup="true" type="button"
|
||||||
aria-controls="dropdown-menu" @click="toggle" :class="button_classes">
|
aria-controls="dropdown-menu" @click="toggle" :class="button_classes">
|
||||||
<span class="icon" v-if="icons"><i :class="icons" /></span>
|
<span class="icon" v-if="icons"><i :class="icons" /></span>
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,11 @@ const props = defineProps({
|
||||||
default: '',
|
default: '',
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
preset: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
useUrl: {
|
useUrl: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|
@ -73,7 +78,16 @@ const handle_event = (e: KeyboardEvent) => {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
document.addEventListener('keydown', handle_event)
|
document.addEventListener('keydown', handle_event)
|
||||||
|
|
||||||
const url = props.useUrl ? props.link : '/api/yt-dlp/url/info?url=' + encodePath(props.link)
|
let url = props.useUrl ? props.link : '/api/yt-dlp/url/info';
|
||||||
|
|
||||||
|
if (!props.useUrl) {
|
||||||
|
let params = new URLSearchParams();
|
||||||
|
if (props.preset) {
|
||||||
|
params.append('preset', props.preset);
|
||||||
|
}
|
||||||
|
params.append('url', props.link);
|
||||||
|
url += '?' + params.toString();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
</template>
|
</template>
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -357,7 +357,8 @@
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)" v-if="!config.app.basic_mode">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)"
|
||||||
|
v-if="!config.app.basic_mode">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
|
||||||
|
|
@ -76,16 +76,16 @@
|
||||||
<span>Opts</span>
|
<span>Opts</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="column is-12" v-if="!hasFormatInConfig && get_preset(form.preset)?.description">
|
||||||
<div class="column is-12" v-if="get_preset(form.preset)?.description">
|
<div class="is-overflow-auto" style="max-height: 150px;">
|
||||||
<div class="is-overflow-auto" style="max-height: 150px;">
|
<div class="is-ellipsis is-clickable" @click="expand_description">
|
||||||
<div class="is-ellipsis is-clickable" @click="expand_description">
|
<span class="icon"><i class="fa-solid fa-info" /></span> {{ get_preset(form.preset)?.description }}
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span> {{ get_preset(form.preset)?.description }}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns is-multiline is-mobile" v-if="showAdvanced && !config.app.basic_mode">
|
|
||||||
|
|
||||||
|
<div class="columns is-multiline is-mobile" v-if="showAdvanced && !config.app.basic_mode">
|
||||||
<div class="column is-4-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
<div class="column is-4-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline is-unselectable">
|
<label class="label is-inline is-unselectable">
|
||||||
|
|
@ -172,11 +172,29 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-12">
|
<div class="column is-12 is-hidden-tablet">
|
||||||
<div class="field is-grouped is-justify-self-end">
|
<Dropdown icons="fa-solid fa-cogs" label="Actions">
|
||||||
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', form.url, form.preset)">
|
||||||
|
<span class="icon has-text-info"><i class="fa-solid fa-info" /></span>
|
||||||
|
<span>yt-dlp Information</span>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<NuxtLink class="dropdown-item" @click="removeFromArchive(form.url)">
|
||||||
|
<span class="icon has-text-warning"><i class="fa-solid fa-box-archive" /></span>
|
||||||
|
<span>Remove from archive</span>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<hr class="dropdown-divider" />
|
||||||
|
<NuxtLink class="dropdown-item" @click="resetConfig">
|
||||||
|
<span class="icon has-text-danger"><i class="fa-solid fa-rotate-left" /></span>
|
||||||
|
<span>Reset local settings</span>
|
||||||
|
</NuxtLink>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12">
|
||||||
|
<div class="field is-grouped is-justify-self-end is-hidden-mobile">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button type="button" class="button is-info" @click="emitter('getInfo', form.url)"
|
<button type="button" class="button is-info" @click="emitter('getInfo', form.url, form.preset)"
|
||||||
:class="{ 'is-loading': !socket.isConnected }"
|
:class="{ 'is-loading': !socket.isConnected }"
|
||||||
:disabled="!socket.isConnected || addInProgress || !form?.url">
|
:disabled="!socket.isConnected || addInProgress || !form?.url">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
|
@ -200,6 +218,7 @@
|
||||||
<span>Reset</span>
|
<span>Reset</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -363,6 +382,18 @@ const convertOptions = async args => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUpdated(async () => {
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
if ('' === form.value?.preset) {
|
||||||
|
form.value.preset = config.app.default_preset
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.value?.preset && !config.presets.some(p => p.name === form.value.preset)) {
|
||||||
|
form.value.preset = config.app.default_preset
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
|
|
@ -370,6 +401,10 @@ onMounted(async () => {
|
||||||
form.value.preset = config.app.default_preset
|
form.value.preset = config.app.default_preset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (form.value?.preset && !config.presets.some(p => p.name === form.value.preset)) {
|
||||||
|
form.value.preset = config.app.default_preset
|
||||||
|
}
|
||||||
|
|
||||||
if (props?.item) {
|
if (props?.item) {
|
||||||
Object.keys(props.item).forEach(key => {
|
Object.keys(props.item).forEach(key => {
|
||||||
if (key in form.value) {
|
if (key in form.value) {
|
||||||
|
|
@ -400,7 +435,6 @@ const filter_presets = (flag = true) => config.presets.filter(item => item.defau
|
||||||
const get_preset = name => config.presets.find(item => item.name === name)
|
const get_preset = name => config.presets.find(item => item.name === name)
|
||||||
const expand_description = e => toggleClass(e.target, ['is-ellipsis', 'is-pre-wrap'])
|
const expand_description = e => toggleClass(e.target, ['is-ellipsis', 'is-pre-wrap'])
|
||||||
|
|
||||||
|
|
||||||
const removeFromArchive = async url => {
|
const removeFromArchive = async url => {
|
||||||
try {
|
try {
|
||||||
const req = await request(`/api/archive/0`, {
|
const req = await request(`/api/archive/0`, {
|
||||||
|
|
|
||||||
|
|
@ -120,18 +120,19 @@
|
||||||
<span>Cancel Item</span>
|
<span>Cancel Item</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<hr class="dropdown-divider" v-if="!config.app.basic_mode" />
|
<template v-if="!config.app.basic_mode">
|
||||||
|
<hr class="dropdown-divider" />
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)" v-if="!config.app.basic_mode">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)"
|
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||||
v-if="!config.app.basic_mode">
|
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
<span>Local Information</span>
|
||||||
<span>Local Information</span>
|
</NuxtLink>
|
||||||
</NuxtLink>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -227,10 +228,11 @@
|
||||||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||||
<span>Play video</span>
|
<span>Play video</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" v-if="!config.app.basic_mode"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)" v-if="!config.app.basic_mode">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)"
|
||||||
|
v-if="!config.app.basic_mode">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NewDownload v-if="config.showForm || config.app.basic_mode" @getInfo="url => get_info = url" :item="item_form"
|
<NewDownload v-if="config.showForm || config.app.basic_mode"
|
||||||
|
@getInfo="(url: string, preset: string = '') => view_info(url, false, preset)" :item="item_form"
|
||||||
@clear_form="item_form = {}" @remove_archive="" />
|
@clear_form="item_form = {}" @remove_archive="" />
|
||||||
<Queue @getInfo="(url: string) => view_info(url, false)" :thumbnails="show_thumbnail" :query="query"
|
<Queue @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)" :thumbnails="show_thumbnail"
|
||||||
|
:query="query" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
||||||
|
<History @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)"
|
||||||
|
@add_new="item => toNewDownload(item)" :query="query" :thumbnails="show_thumbnail"
|
||||||
@getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
@getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
||||||
<History @getInfo="(url: string) => view_info(url, false)" @add_new="item => toNewDownload(item)" :query="query"
|
<GetInfo v-if="info_view.url" :link="info_view.url" :preset="info_view.preset" :useUrl="info_view.useUrl"
|
||||||
:thumbnails="show_thumbnail" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)"
|
@closeModel="close_info()" />
|
||||||
@clear_search="query = ''" />
|
|
||||||
<GetInfo v-if="get_info" :link="get_info" :useUrl="get_info_use_url" @closeModel="close_info()" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -83,8 +85,11 @@ const bg_opacity = useStorage<number>('random_bg_opacity', 0.85)
|
||||||
const display_style = useStorage<string>('display_style', 'cards')
|
const display_style = useStorage<string>('display_style', 'cards')
|
||||||
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
|
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
|
||||||
|
|
||||||
const get_info = ref<string>('')
|
const info_view = ref({
|
||||||
const get_info_use_url = ref<boolean>(false)
|
url: '',
|
||||||
|
preset: '',
|
||||||
|
useUrl: false,
|
||||||
|
}) as Ref<{ url: string, preset: string, useUrl: boolean }>
|
||||||
const item_form = ref({})
|
const item_form = ref({})
|
||||||
const query = ref()
|
const query = ref()
|
||||||
const toggleFilter = ref(false)
|
const toggleFilter = ref(false)
|
||||||
|
|
@ -126,16 +131,18 @@ const pauseDownload = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const close_info = () => {
|
const close_info = () => {
|
||||||
get_info.value = ''
|
info_view.value.url = ''
|
||||||
get_info_use_url.value = false
|
info_view.value.preset = ''
|
||||||
|
info_view.value.useUrl = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const view_info = (url: string, useUrl: boolean = false) => {
|
const view_info = (url: string, useUrl: boolean = false, preset: string = '') => {
|
||||||
get_info.value = url
|
info_view.value.url = url
|
||||||
get_info_use_url.value = useUrl
|
info_view.value.useUrl = useUrl
|
||||||
|
info_view.value.preset = preset
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(get_info, v => {
|
watch(() => info_view.value.url, v => {
|
||||||
if (!bg_enable.value) {
|
if (!bg_enable.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue