simplify API calls.
This commit is contained in:
parent
e5f2a09cc7
commit
922d194d7a
17 changed files with 66 additions and 72 deletions
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -18,6 +18,7 @@
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"cwd": "${workspaceFolder}/ui",
|
"cwd": "${workspaceFolder}/ui",
|
||||||
"env": {
|
"env": {
|
||||||
|
"NUXT_API_URL": "http://localhost:8081/api/",
|
||||||
"NUXT_PUBLIC_WSS": ":8081/",
|
"NUXT_PUBLIC_WSS": ":8081/",
|
||||||
},
|
},
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
|
|
@ -34,13 +35,13 @@
|
||||||
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
||||||
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
|
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
|
||||||
"PYDEVD_DISABLE_FILE_VALIDATION": "1",
|
"PYDEVD_DISABLE_FILE_VALIDATION": "1",
|
||||||
"YTP_URL_HOST": "http://localhost:8081",
|
|
||||||
"YTP_MAX_WORKERS": "2",
|
"YTP_MAX_WORKERS": "2",
|
||||||
"YTP_IGNORE_UI": "true",
|
"YTP_IGNORE_UI": "true",
|
||||||
"YTP_PIP_IGNORE_UPDATES": "true",
|
"YTP_PIP_IGNORE_UPDATES": "true",
|
||||||
"YTP_LOG_LEVEL": "DEBUG",
|
"YTP_LOG_LEVEL": "DEBUG",
|
||||||
"YTP_DEBUG": "true",
|
"YTP_DEBUG": "true",
|
||||||
"YTP_YTDL_DEBUG": "false",
|
"YTP_YTDL_DEBUG": "false",
|
||||||
|
"YTP_ACCESS_LOG": "true",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -54,7 +55,6 @@
|
||||||
"YTP_CONFIG_PATH": "${workspaceFolder}/var/config",
|
"YTP_CONFIG_PATH": "${workspaceFolder}/var/config",
|
||||||
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
||||||
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
|
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
|
||||||
"YTP_URL_HOST": "http://localhost:8081",
|
|
||||||
"YTP_LOG_LEVEL": "DEBUG",
|
"YTP_LOG_LEVEL": "DEBUG",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ Certain values can be set via environment variables, using the `-e` parameter on
|
||||||
* __YTP_DOWNLOAD_PATH__: path to where the downloads will be saved. Defaults to `/downloads` in the docker image, and `./var/downloads` otherwise.
|
* __YTP_DOWNLOAD_PATH__: path to where the downloads will be saved. Defaults to `/downloads` in the docker image, and `./var/downloads` otherwise.
|
||||||
* __YTP_TEMP_PATH__: path where intermediary download files will be saved. Defaults to `/tmp` in the docker image, and `./var/tmp` otherwise.
|
* __YTP_TEMP_PATH__: path where intermediary download files will be saved. Defaults to `/tmp` in the docker image, and `./var/tmp` otherwise.
|
||||||
* __YTP_TEMP_KEEP__: Whether to keep the Individual video temp directory or remove it. Defaults to `false`.
|
* __YTP_TEMP_KEEP__: Whether to keep the Individual video temp directory or remove it. Defaults to `false`.
|
||||||
* __YTP_URL_PREFIX__: base path for the web server (for use when hosting behind a reverse proxy). Defaults to `/`.
|
|
||||||
* __YTP_OUTPUT_TEMPLATE__: the template for the filenames of the downloaded videos, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s.%(ext)s`. This will be the default for all downloads unless the request include output template.
|
* __YTP_OUTPUT_TEMPLATE__: the template for the filenames of the downloaded videos, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s.%(ext)s`. This will be the default for all downloads unless the request include output template.
|
||||||
* __YTP_OUTPUT_TEMPLATE_CHAPTER__: the template for the filenames of the downloaded videos, when split into chapters via postprocessors, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s - %(section_number)s %(section_title)s.%(ext)s.`
|
* __YTP_OUTPUT_TEMPLATE_CHAPTER__: the template for the filenames of the downloaded videos, when split into chapters via postprocessors, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s - %(section_number)s %(section_title)s.%(ext)s.`
|
||||||
* __YTP_KEEP_ARCHIVE__: Whether to keep history of downloaded videos to prevent downloading same file multiple times. Defaults to `true`.
|
* __YTP_KEEP_ARCHIVE__: Whether to keep history of downloaded videos to prevent downloading same file multiple times. Defaults to `true`.
|
||||||
|
|
@ -102,8 +101,6 @@ Certain values can be set via environment variables, using the `-e` parameter on
|
||||||
|
|
||||||
It's advisable to run YTPTube behind a reverse proxy, if authentication and/or HTTPS support are required.
|
It's advisable to run YTPTube behind a reverse proxy, if authentication and/or HTTPS support are required.
|
||||||
|
|
||||||
When running behind a reverse proxy which remaps the URL (i.e. serves YTPTube under a subdirectory and not under root), don't forget to set the `YTP_URL_PREFIX` environment variable to the correct value.
|
|
||||||
|
|
||||||
### NGINX
|
### NGINX
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class HttpAPI(common):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
file = os.path.join(root, file)
|
file = os.path.join(root, file)
|
||||||
urlPath = f"{self.config.url_prefix}{file.replace(f'{staticDir}/', '')}"
|
urlPath = f"/{file.replace(f'{staticDir}/', '')}"
|
||||||
|
|
||||||
content = open(file, "rb").read()
|
content = open(file, "rb").read()
|
||||||
contentType = self.extToMime.get(os.path.splitext(file)[1], MIME.from_file(file))
|
contentType = self.extToMime.get(os.path.splitext(file)[1], MIME.from_file(file))
|
||||||
|
|
@ -149,10 +149,10 @@ class HttpAPI(common):
|
||||||
method = getattr(self, attr_name)
|
method = getattr(self, attr_name)
|
||||||
if hasattr(method, "_http_method") and hasattr(method, "_http_path"):
|
if hasattr(method, "_http_method") and hasattr(method, "_http_path"):
|
||||||
http_path = method._http_path
|
http_path = method._http_path
|
||||||
if http_path.startswith("/") and self.config.url_prefix.endswith("/"):
|
if http_path.startswith("/"):
|
||||||
http_path = method._http_path[1:]
|
http_path = method._http_path[1:]
|
||||||
|
|
||||||
self.routes.route(method._http_method, self.config.url_prefix + http_path)(method)
|
self.routes.route(method._http_method, f"/{http_path}")(method)
|
||||||
|
|
||||||
async def on_prepare(request: Request, response: Response):
|
async def on_prepare(request: Request, response: Response):
|
||||||
if "Server" in response.headers:
|
if "Server" in response.headers:
|
||||||
|
|
@ -160,14 +160,10 @@ class HttpAPI(common):
|
||||||
|
|
||||||
if "Origin" in request.headers:
|
if "Origin" in request.headers:
|
||||||
response.headers["Access-Control-Allow-Origin"] = request.headers["Origin"]
|
response.headers["Access-Control-Allow-Origin"] = request.headers["Origin"]
|
||||||
response.headers["Access-Control-Allow-Headers"] = "Content-Type"
|
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
|
||||||
response.headers["Access-Control-Allow-Methods"] = "PATCH, PUT, POST, DELETE"
|
response.headers["Access-Control-Allow-Methods"] = "GET, PATCH, PUT, POST, DELETE"
|
||||||
|
|
||||||
if self.config.url_prefix != "/":
|
self.routes.static("/api/download/", self.config.download_path)
|
||||||
self.routes.route("GET", "/")(lambda _: web.HTTPFound(self.config.url_prefix))
|
|
||||||
self.routes.get(self.config.url_prefix[:-1])(lambda _: web.HTTPFound(self.config.url_prefix))
|
|
||||||
|
|
||||||
self.routes.static(f"{self.config.url_prefix}api/download/", self.config.download_path)
|
|
||||||
self.preloadStatic(app)
|
self.preloadStatic(app)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -215,6 +211,10 @@ class HttpAPI(common):
|
||||||
|
|
||||||
return middleware_handler
|
return middleware_handler
|
||||||
|
|
||||||
|
@route("OPTIONS", "/{path:.*}")
|
||||||
|
async def add_coors(self, _: Request) -> Response:
|
||||||
|
return web.json_response(data={"status": "ok"}, status=web.HTTPOk.status_code)
|
||||||
|
|
||||||
@route("GET", "api/ping")
|
@route("GET", "api/ping")
|
||||||
async def ping(self, _) -> Response:
|
async def ping(self, _) -> Response:
|
||||||
await self.queue.test()
|
await self.queue.test()
|
||||||
|
|
@ -564,9 +564,7 @@ class HttpAPI(common):
|
||||||
raise web.HTTPBadRequest(text="file is required.")
|
raise web.HTTPBadRequest(text="file is required.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = await Playlist(url=f"{self.config.url_host}{self.config.url_prefix}").make(
|
text = await Playlist(url="/").make(download_path=self.config.download_path, file=file)
|
||||||
download_path=self.config.download_path, file=file
|
|
||||||
)
|
|
||||||
if isinstance(text, Response):
|
if isinstance(text, Response):
|
||||||
return text
|
return text
|
||||||
except StreamingError as e:
|
except StreamingError as e:
|
||||||
|
|
@ -604,7 +602,7 @@ class HttpAPI(common):
|
||||||
duration = float(duration)
|
duration = float(duration)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cls = M3u8(f"{self.config.url_host}{self.config.url_prefix}")
|
cls = M3u8("/")
|
||||||
if "subtitle" in mode:
|
if "subtitle" in mode:
|
||||||
text = await cls.make_subtitle(self.config.download_path, file, duration)
|
text = await cls.make_subtitle(self.config.download_path, file, duration)
|
||||||
else:
|
else:
|
||||||
|
|
@ -707,22 +705,6 @@ class HttpAPI(common):
|
||||||
status=web.HTTPOk.status_code,
|
status=web.HTTPOk.status_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
@route("OPTIONS", "api/add")
|
|
||||||
async def add_cors(self, _: Request) -> Response:
|
|
||||||
return web.json_response(data={"status": "ok"}, status=web.HTTPOk.status_code)
|
|
||||||
|
|
||||||
@route("OPTIONS", "api/tasks")
|
|
||||||
async def cors_add_tasks(self, _: Request) -> Response:
|
|
||||||
return web.json_response(data={"status": "ok"}, status=web.HTTPOk.status_code)
|
|
||||||
|
|
||||||
@route("OPTIONS", "api/yt-dlp/convert")
|
|
||||||
async def cors_ytdlp_convert(self, _: Request) -> Response:
|
|
||||||
return web.json_response(data={"status": "ok"}, status=web.HTTPOk.status_code)
|
|
||||||
|
|
||||||
@route("OPTIONS", "api/delete")
|
|
||||||
async def delete_cors(self, _: Request) -> Response:
|
|
||||||
return web.json_response(data={"status": "ok"}, status=web.HTTPOk.status_code)
|
|
||||||
|
|
||||||
@route("GET", "/")
|
@route("GET", "/")
|
||||||
async def index(self, _) -> Response:
|
async def index(self, _) -> Response:
|
||||||
if "/index.html" not in self.staticHolder:
|
if "/index.html" not in self.staticHolder:
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class HttpSocket(common):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def attach(self, app: web.Application):
|
def attach(self, app: web.Application):
|
||||||
self.sio.attach(app, socketio_path=self.config.url_prefix + "socket.io")
|
self.sio.attach(app, socketio_path=self.config.url_socketio)
|
||||||
|
|
||||||
for attr_name in dir(self):
|
for attr_name in dir(self):
|
||||||
method = getattr(self, attr_name)
|
method = getattr(self, attr_name)
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,7 @@ class Config:
|
||||||
temp_keep: bool = False
|
temp_keep: bool = False
|
||||||
"""Keep temporary files after the download is complete."""
|
"""Keep temporary files after the download is complete."""
|
||||||
|
|
||||||
url_host: str = ""
|
url_socketio: str = "/socket.io"
|
||||||
"""The host to bind the server to."""
|
|
||||||
url_prefix: str = ""
|
|
||||||
"""The prefix to use for the server URL."""
|
|
||||||
url_socketio: str = "{url_prefix}socket.io"
|
|
||||||
"""The URL to use for the socket.io server."""
|
"""The URL to use for the socket.io server."""
|
||||||
|
|
||||||
output_template: str = "%(title)s.%(ext)s"
|
output_template: str = "%(title)s.%(ext)s"
|
||||||
|
|
@ -192,9 +188,7 @@ class Config:
|
||||||
"output_template",
|
"output_template",
|
||||||
"ytdlp_version",
|
"ytdlp_version",
|
||||||
"version",
|
"version",
|
||||||
"url_host",
|
|
||||||
"started",
|
"started",
|
||||||
"url_prefix",
|
|
||||||
"remove_files",
|
"remove_files",
|
||||||
"ui_update_title",
|
"ui_update_title",
|
||||||
"max_workers",
|
"max_workers",
|
||||||
|
|
@ -275,9 +269,6 @@ class Config:
|
||||||
if k in self._int_vars:
|
if k in self._int_vars:
|
||||||
setattr(self, k, int(v))
|
setattr(self, k, int(v))
|
||||||
|
|
||||||
if not self.url_prefix.endswith("/"):
|
|
||||||
self.url_prefix += "/"
|
|
||||||
|
|
||||||
numeric_level = getattr(logging, self.log_level.upper(), None)
|
numeric_level = getattr(logging, self.log_level.upper(), None)
|
||||||
if not isinstance(numeric_level, int):
|
if not isinstance(numeric_level, int):
|
||||||
raise ValueError(f"Invalid log level '{self.log_level}' specified.")
|
raise ValueError(f"Invalid log level '{self.log_level}' specified.")
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
image: {
|
image: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -38,7 +40,6 @@ const props = defineProps({
|
||||||
|
|
||||||
const cache = useSessionCache()
|
const cache = useSessionCache()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const config = useConfigStore()
|
|
||||||
const url = ref()
|
const url = ref()
|
||||||
const error = ref(false)
|
const error = ref(false)
|
||||||
const isPreloading = ref(false)
|
const isPreloading = ref(false)
|
||||||
|
|
@ -53,8 +54,8 @@ const defaultLoader = async () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/thumbnail?url=' + encodePath(props.image), {
|
const response = await request('/api/thumbnail?url=' + encodePath(props.image), {
|
||||||
signal: cancelRequest.signal
|
signal: cancelRequest.signal,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (200 !== response.status) {
|
if (200 !== response.status) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const config = useConfigStore()
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const emitter = defineEmits(['closeModel'])
|
const emitter = defineEmits(['closeModel'])
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const data = ref({})
|
const data = ref({})
|
||||||
|
|
@ -46,10 +47,10 @@ const eventFunc = e => {
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.addEventListener('keydown', eventFunc)
|
window.addEventListener('keydown', eventFunc)
|
||||||
const url = config.app.url_host + config.app.url_prefix + 'api/url/info?url=' + encodePath(props.link)
|
const url = '/api/url/info?url=' + encodePath(props.link)
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
const response = await fetch(url);
|
const response = await request(url, { credentials: 'include' });
|
||||||
data.value = await response.json();
|
data.value = await response.json();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
|
|
|
||||||
|
|
@ -99,14 +99,11 @@
|
||||||
<figure class="image is-3by1">
|
<figure class="image is-3by1">
|
||||||
<span v-if="'finished' === item.status" @click="playVideo(item)" class="play-overlay">
|
<span v-if="'finished' === item.status" @click="playVideo(item)" class="play-overlay">
|
||||||
<div class="play-icon"></div>
|
<div class="play-icon"></div>
|
||||||
<img
|
<img :src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" v-if="item.extras?.thumbnail" />
|
||||||
:src="config.app.url_host + config.app.url_prefix + 'api/thumbnail?url=' + encodePath(item.extras.thumbnail)"
|
|
||||||
v-if="item.extras?.thumbnail" />
|
|
||||||
<img v-else src="/images/placeholder.png" />
|
<img v-else src="/images/placeholder.png" />
|
||||||
</span>
|
</span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<img v-if="item.extras?.thumbnail"
|
<img v-if="item.extras?.thumbnail" :src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
||||||
:src="config.app.url_host + config.app.url_prefix + 'api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
|
||||||
<img v-else src="/images/placeholder.png" />
|
<img v-else src="/images/placeholder.png" />
|
||||||
</template>
|
</template>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
@ -276,7 +273,7 @@ const playVideo = item => {
|
||||||
video_link.value = makeDownload(config, item, 'm3u8')
|
video_link.value = makeDownload(config, item, 'm3u8')
|
||||||
video_title.value = item.title
|
video_title.value = item.title
|
||||||
if (item.extras?.thumbnail) {
|
if (item.extras?.thumbnail) {
|
||||||
video_thumbnail.value = config.app.url_host + config.app.url_prefix + 'api/thumbnail?url=' + encodePath(item.extras.thumbnail)
|
video_thumbnail.value = '/api/thumbnail?url=' + encodePath(item.extras.thumbnail)
|
||||||
}
|
}
|
||||||
if (item.extras?.channel) {
|
if (item.extras?.channel) {
|
||||||
video_artist.value = item.extras.channel
|
video_artist.value = item.extras.channel
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo'])
|
const emitter = defineEmits(['getInfo'])
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|
@ -155,7 +156,7 @@ const addInProgress = ref(false)
|
||||||
const addDownload = async () => {
|
const addDownload = async () => {
|
||||||
// -- send request to convert cli options to JSON
|
// -- send request to convert cli options to JSON
|
||||||
if (ytdlpConfig.value && ytdlpConfig.value.length > 2 && !ytdlpConfig.value.trim().startsWith('{')) {
|
if (ytdlpConfig.value && ytdlpConfig.value.length > 2 && !ytdlpConfig.value.trim().startsWith('{')) {
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/yt-dlp/convert', {
|
const response = await request('/api/yt-dlp/convert', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@
|
||||||
<div v-if="false === hideThumbnail" class="card-image">
|
<div v-if="false === hideThumbnail" class="card-image">
|
||||||
<figure class="image is-3by1" v-if="item.extras?.thumbnail">
|
<figure class="image is-3by1" v-if="item.extras?.thumbnail">
|
||||||
<img :alt="item.title"
|
<img :alt="item.title"
|
||||||
:src="config.app.url_host + config.app.url_prefix + 'api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
:src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
||||||
</figure>
|
</figure>
|
||||||
<figure class="image is-3by1" v-else>
|
<figure class="image is-3by1" v-else>
|
||||||
<img :src="config.app.url_host + config.app.url_prefix + 'images/placeholder.png'" />
|
<img :src="'/images/placeholder.png'" />
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { parseExpression } from 'cron-parser'
|
import { parseExpression } from 'cron-parser'
|
||||||
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const emitter = defineEmits(['cancel', 'submit']);
|
const emitter = defineEmits(['cancel', 'submit']);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
@ -242,7 +243,7 @@ const checkInfo = async () => {
|
||||||
|
|
||||||
// -- send request to convert cli options to JSON
|
// -- send request to convert cli options to JSON
|
||||||
if (form.ytdlp_config && form.ytdlp_config.length > 2 && !form.ytdlp_config.trim().startsWith('{')) {
|
if (form.ytdlp_config && form.ytdlp_config.length > 2 && !form.ytdlp_config.trim().startsWith('{')) {
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/yt-dlp/convert', {
|
const response = await request('/api/yt-dlp/convert', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ import 'assets/css/style.css'
|
||||||
import 'assets/css/all.css'
|
import 'assets/css/all.css'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const Year = new Date().getFullYear()
|
const Year = new Date().getFullYear()
|
||||||
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')())
|
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')())
|
||||||
|
|
@ -159,7 +160,7 @@ const checkCookies = async () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isChecking.value = true
|
isChecking.value = true
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/youtube/auth')
|
const response = await request('/api/youtube/auth')
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success('Succuss. ' + data.message)
|
toast.success('Succuss. ' + data.message)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
let extraNitro = {}
|
||||||
|
try {
|
||||||
|
const API_URL = import.meta.env.NUXT_API_URL;
|
||||||
|
if (API_URL) {
|
||||||
|
extraNitro = {
|
||||||
|
devProxy: {
|
||||||
|
'/api/': {
|
||||||
|
target: API_URL,
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
ssr: false,
|
ssr: false,
|
||||||
devtools: { enabled: false },
|
devtools: { enabled: false },
|
||||||
|
|
@ -48,7 +65,8 @@ export default defineNuxtConfig({
|
||||||
nitro: {
|
nitro: {
|
||||||
output: {
|
output: {
|
||||||
publicDir: path.join(__dirname, 'exported')
|
publicDir: path.join(__dirname, 'exported')
|
||||||
}
|
},
|
||||||
|
...extraNitro,
|
||||||
},
|
},
|
||||||
|
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ div.is-centered {
|
||||||
<script setup>
|
<script setup>
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { parseExpression } from 'cron-parser'
|
import { parseExpression } from 'cron-parser'
|
||||||
|
import { request } from '~/utils/index'
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|
@ -151,7 +152,8 @@ watch(() => socket.isConnected, async () => {
|
||||||
const reloadContent = async (fromMounted = false) => {
|
const reloadContent = async (fromMounted = false) => {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/tasks')
|
const response = await request('/api/tasks')
|
||||||
|
|
||||||
if (fromMounted && !response.ok) {
|
if (fromMounted && !response.ok) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +184,7 @@ const resetTask = (closeForm = false) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateTasks = async tasks => {
|
const updateTasks = async tasks => {
|
||||||
const response = await fetch(config.app.url_host + config.app.url_prefix + 'api/tasks', {
|
const response = await request('/api/tasks', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ const CONFIG_KEYS = {
|
||||||
ytdlp_version: '',
|
ytdlp_version: '',
|
||||||
max_workers: 1,
|
max_workers: 1,
|
||||||
version: '',
|
version: '',
|
||||||
url_host: '',
|
|
||||||
url_prefix: '',
|
|
||||||
has_cookies: false,
|
has_cookies: false,
|
||||||
basic_mode: true,
|
basic_mode: true,
|
||||||
default_preset: 'default',
|
default_preset: 'default',
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||||
const isConnected = ref(false);
|
const isConnected = ref(false);
|
||||||
|
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
socket.value = io(runtimeConfig.public.wss)
|
socket.value = io(runtimeConfig.public.wss, { withCredentials: true })
|
||||||
|
|
||||||
socket.value.on('connect', () => isConnected.value = true);
|
socket.value.on('connect', () => isConnected.value = true);
|
||||||
socket.value.on('disconnect', () => isConnected.value = false);
|
socket.value.on('disconnect', () => isConnected.value = false);
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ const encodePath = item => {
|
||||||
* And prefix the URL with the API URL and path.
|
* And prefix the URL with the API URL and path.
|
||||||
*
|
*
|
||||||
* @param {string} url - The URL to request
|
* @param {string} url - The URL to request
|
||||||
* @param {object} options - The request options
|
* @param {RequestInit} options - The request options
|
||||||
*
|
*
|
||||||
* @returns {Promise<Response>} - The response from the API
|
* @returns {Promise<Response>} - The response from the API
|
||||||
*/
|
*/
|
||||||
|
|
@ -254,7 +254,11 @@ const request = (url, options = {}) => {
|
||||||
options.headers['Accept'] = 'application/json'
|
options.headers['Accept'] = 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(url.startsWith('/') ? runtimeConfig.public.domain + url : url, options)
|
if (url.startsWith('/')) {
|
||||||
|
options.credentials = 'same-origin'
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url.startsWith('/') ? eTrim(runtimeConfig.public.domain, '/') + '/' + sTrim(url, '/') : url, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -353,7 +357,7 @@ const makeDownload = (config, item, base = 'api/download') => {
|
||||||
baseDir += item.folder + '/';
|
baseDir += item.folder + '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = config.app.url_host + encodePath(config.app.url_prefix + baseDir + item.filename);
|
let url = `/${sTrim(baseDir, '/')}${encodePath(item.filename)}`;
|
||||||
return ('m3u8' === base) ? url + '.m3u8' : url;
|
return ('m3u8' === base) ? url + '.m3u8' : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue