Merge pull request #361 from arabcoders/dev
Some checks failed
Build WebView wrappers / build (amd64, ubuntu-latest) (push) Has been cancelled
Build WebView wrappers / build (amd64, windows-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, macos-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, ubuntu-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, windows-latest) (push) Has been cancelled
Some checks failed
Build WebView wrappers / build (amd64, ubuntu-latest) (push) Has been cancelled
Build WebView wrappers / build (amd64, windows-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, macos-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, ubuntu-latest) (push) Has been cancelled
Build WebView wrappers / build (arm64, windows-latest) (push) Has been cancelled
Add force download switch
This commit is contained in:
commit
c310ae9abc
7 changed files with 80 additions and 35 deletions
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
|
|
@ -27,6 +27,7 @@
|
||||||
"program": "app/main.py",
|
"program": "app/main.py",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
|
"subProcess": true,
|
||||||
"env": {
|
"env": {
|
||||||
"YTP_CONFIG_PATH": "${workspaceFolder}/var/config",
|
"YTP_CONFIG_PATH": "${workspaceFolder}/var/config",
|
||||||
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
||||||
|
|
@ -34,8 +35,6 @@
|
||||||
"PYDEVD_DISABLE_FILE_VALIDATION": "1",
|
"PYDEVD_DISABLE_FILE_VALIDATION": "1",
|
||||||
"YTP_IGNORE_UI": "true"
|
"YTP_IGNORE_UI": "true"
|
||||||
},
|
},
|
||||||
"subProcess": true,
|
|
||||||
"postDebugTask": "kill-debugpy"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Node: Generate UI",
|
"name": "Node: Generate UI",
|
||||||
|
|
|
||||||
22
.vscode/tasks.json
vendored
22
.vscode/tasks.json
vendored
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "kill-debugpy",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "pkill -f debugpy || true",
|
|
||||||
"problemMatcher": [],
|
|
||||||
"presentation": {
|
|
||||||
"echo": false,
|
|
||||||
"reveal": "never",
|
|
||||||
"focus": false,
|
|
||||||
"panel": "dedicated",
|
|
||||||
"showReuseMessage": false,
|
|
||||||
"clear": false
|
|
||||||
},
|
|
||||||
"runOptions": {
|
|
||||||
"runOn": "folderOpen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -317,4 +317,5 @@ Certain configuration values can be set via environment variables, using the `-e
|
||||||
| YTP_TASKS_HANDLER_TIMER | The cron expression for the tasks handler timer | `15 */1 * * *` |
|
| YTP_TASKS_HANDLER_TIMER | The cron expression for the tasks handler timer | `15 */1 * * *` |
|
||||||
| YTP_PLAYLIST_ITEMS_CONCURRENCY | The number of playlist items be to processed at same time | `1` |
|
| YTP_PLAYLIST_ITEMS_CONCURRENCY | The number of playlist items be to processed at same time | `1` |
|
||||||
| YTP_TEMP_DISABLED | Disable temp files handling. | `false` |
|
| YTP_TEMP_DISABLED | Disable temp files handling. | `false` |
|
||||||
|
| YTP_DOWNLOAD_PATH_DEPTH | How many subdirectories to show in auto complete. | `1` |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1405,6 +1405,32 @@ def find_unpickleable(obj, name="root", seen=None):
|
||||||
find_unpickleable(value, f"{name}.{attr}", seen)
|
find_unpickleable(value, f"{name}.{attr}", seen)
|
||||||
except Exception as ie:
|
except Exception as ie:
|
||||||
LOG.error(f"[ERROR] Accessing {name}.{attr}: {ie}")
|
LOG.error(f"[ERROR] Accessing {name}.{attr}: {ie}")
|
||||||
elif isinstance(obj, (list, tuple, set)):
|
elif isinstance(obj, list | tuple | set):
|
||||||
for idx, item in enumerate(obj):
|
for idx, item in enumerate(obj):
|
||||||
find_unpickleable(item, f"{name}[{idx}]", seen)
|
find_unpickleable(item, f"{name}[{idx}]", seen)
|
||||||
|
|
||||||
|
|
||||||
|
def list_folders(path: Path, base: Path, depth_limit: int) -> list[str]:
|
||||||
|
"""
|
||||||
|
List all folders relative to a base path, up to a specified depth limit.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (Path): The path to start listing folders from.
|
||||||
|
base (Path): The base path to which the folders should be relative.
|
||||||
|
depth_limit (int): The maximum depth to traverse from the base path.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[str]: A list of folder paths relative to the base path, up to the specified
|
||||||
|
|
||||||
|
"""
|
||||||
|
rel_depth: int = len(path.relative_to(base).parts)
|
||||||
|
if rel_depth > depth_limit:
|
||||||
|
return []
|
||||||
|
|
||||||
|
folders: list[str] = []
|
||||||
|
for entry in path.iterdir():
|
||||||
|
if entry.is_dir():
|
||||||
|
folders.append(str(entry.relative_to(base)))
|
||||||
|
folders.extend(list_folders(entry, base, depth_limit))
|
||||||
|
|
||||||
|
return folders
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ class Config:
|
||||||
download_path: str = "."
|
download_path: str = "."
|
||||||
"""The path to the download directory."""
|
"""The path to the download directory."""
|
||||||
|
|
||||||
|
download_path_depth: int = 2
|
||||||
|
"""How many subdirectories to show in auto complete."""
|
||||||
|
|
||||||
temp_path: str = "/tmp"
|
temp_path: str = "/tmp"
|
||||||
"""The path to the temporary directory."""
|
"""The path to the temporary directory."""
|
||||||
|
|
||||||
|
|
@ -219,6 +222,7 @@ class Config:
|
||||||
"extract_info_timeout",
|
"extract_info_timeout",
|
||||||
"debugpy_port",
|
"debugpy_port",
|
||||||
"playlist_items_concurrency",
|
"playlist_items_concurrency",
|
||||||
|
"download_path_depth",
|
||||||
)
|
)
|
||||||
"The variables that are integers."
|
"The variables that are integers."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from app.library.DownloadQueue import DownloadQueue
|
||||||
from app.library.Events import EventBus, Events
|
from app.library.Events import EventBus, Events
|
||||||
from app.library.Presets import Presets
|
from app.library.Presets import Presets
|
||||||
from app.library.router import RouteType, route
|
from app.library.router import RouteType, route
|
||||||
from app.library.Utils import tail_log
|
from app.library.Utils import list_folders, tail_log
|
||||||
|
|
||||||
LOG: logging.Logger = logging.getLogger(__name__)
|
LOG: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -29,7 +29,11 @@ async def connect(config: Config, queue: DownloadQueue, notify: EventBus, sid: s
|
||||||
"paused": queue.is_paused(),
|
"paused": queue.is_paused(),
|
||||||
}
|
}
|
||||||
|
|
||||||
data["folders"] = [folder.name for folder in Path(config.download_path).iterdir() if folder.is_dir()]
|
data["folders"] = list_folders(
|
||||||
|
path=Path(config.download_path),
|
||||||
|
base=Path(config.download_path),
|
||||||
|
depth_limit=config.download_path_depth-1,
|
||||||
|
)
|
||||||
|
|
||||||
await notify.emit(
|
await notify.emit(
|
||||||
Events.CONNECTED,
|
Events.CONNECTED,
|
||||||
|
|
|
||||||
|
|
@ -88,21 +88,31 @@
|
||||||
</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-3-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input id="auto_start" type="checkbox" v-model="auto_start" :disabled="addInProgress"
|
<input id="force_download" type="checkbox" class="switch is-danger" :checked="forceDownload"
|
||||||
class="switch is-success" />
|
@change="forceDownload = !forceDownload" :disabled="addInProgress" />
|
||||||
<label for="auto_start" class="is-unselectable">
|
<label for="force_download" class="is-unselectable">Force download</label>
|
||||||
{{ auto_start ? 'Auto start' : 'Manual start' }}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span class="is-bold">Whether to start the download automatically or wait.</span>
|
<span class="is-bold">Ignore archive and re-download.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column is-8-tablet is-12-mobile">
|
<div class="column is-3-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||||
|
<div class="field">
|
||||||
|
<input id="auto_start" type="checkbox" v-model="auto_start" :disabled="addInProgress"
|
||||||
|
class="switch is-success" />
|
||||||
|
<label for="auto_start" class="is-unselectable">Auto start</label>
|
||||||
|
</div>
|
||||||
|
<span class="help">
|
||||||
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
<span class="is-bold">Whether to start the download automatically.</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-6-tablet is-12-mobile">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<label for="output_format" class="button is-static is-unselectable">
|
<label for="output_format" class="button is-static is-unselectable">
|
||||||
|
|
@ -268,6 +278,7 @@ const dialog_confirm = ref({
|
||||||
message: '',
|
message: '',
|
||||||
options: [],
|
options: [],
|
||||||
})
|
})
|
||||||
|
const FORCE_FLAG = '--no-download-archive'
|
||||||
|
|
||||||
const addDownload = async () => {
|
const addDownload = async () => {
|
||||||
if (form.value?.cli && '' !== form.value.cli) {
|
if (form.value?.cli && '' !== form.value.cli) {
|
||||||
|
|
@ -454,4 +465,26 @@ const get_output_template = () => {
|
||||||
return config.app.output_template || '%(title)s.%(ext)s'
|
return config.app.output_template || '%(title)s.%(ext)s'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const forceDownload = computed({
|
||||||
|
get(): boolean {
|
||||||
|
return new RegExp(`(^|\\s)${FORCE_FLAG}(\\s|$)`).test(form.value.cli || '')
|
||||||
|
},
|
||||||
|
set(val: boolean): void {
|
||||||
|
const cli = form.value.cli || ''
|
||||||
|
|
||||||
|
if (val) {
|
||||||
|
if (!cli.includes(FORCE_FLAG)) {
|
||||||
|
form.value.cli = cli.trim() + (cli.trim() ? ` ${FORCE_FLAG}` : FORCE_FLAG)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
form.value.cli = cli
|
||||||
|
.replace(new RegExp(`(\\s*)${FORCE_FLAG}(\\s*)`, 'g'), (match, before, after) => {
|
||||||
|
return before && after ? ' ' : ''
|
||||||
|
})
|
||||||
|
.replace(/[ \t]+/g, ' ')
|
||||||
|
.replace(/^[ \t]+|[ \t]+$/g, '')
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue