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",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"subProcess": true,
|
||||
"env": {
|
||||
"YTP_CONFIG_PATH": "${workspaceFolder}/var/config",
|
||||
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
|
||||
|
|
@ -34,8 +35,6 @@
|
|||
"PYDEVD_DISABLE_FILE_VALIDATION": "1",
|
||||
"YTP_IGNORE_UI": "true"
|
||||
},
|
||||
"subProcess": true,
|
||||
"postDebugTask": "kill-debugpy"
|
||||
},
|
||||
{
|
||||
"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_PLAYLIST_ITEMS_CONCURRENCY | The number of playlist items be to processed at same time | `1` |
|
||||
| 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)
|
||||
except Exception as 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):
|
||||
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 = "."
|
||||
"""The path to the download directory."""
|
||||
|
||||
download_path_depth: int = 2
|
||||
"""How many subdirectories to show in auto complete."""
|
||||
|
||||
temp_path: str = "/tmp"
|
||||
"""The path to the temporary directory."""
|
||||
|
||||
|
|
@ -219,6 +222,7 @@ class Config:
|
|||
"extract_info_timeout",
|
||||
"debugpy_port",
|
||||
"playlist_items_concurrency",
|
||||
"download_path_depth",
|
||||
)
|
||||
"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.Presets import Presets
|
||||
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__)
|
||||
|
||||
|
|
@ -29,7 +29,11 @@ async def connect(config: Config, queue: DownloadQueue, notify: EventBus, sid: s
|
|||
"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(
|
||||
Events.CONNECTED,
|
||||
|
|
|
|||
|
|
@ -88,21 +88,31 @@
|
|||
</div>
|
||||
|
||||
<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">
|
||||
<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 ? 'Auto start' : 'Manual start' }}
|
||||
</label>
|
||||
<input id="force_download" type="checkbox" class="switch is-danger" :checked="forceDownload"
|
||||
@change="forceDownload = !forceDownload" :disabled="addInProgress" />
|
||||
<label for="force_download" class="is-unselectable">Force download</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 or wait.</span>
|
||||
<span class="is-bold">Ignore archive and re-download.</span>
|
||||
</span>
|
||||
</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="control">
|
||||
<label for="output_format" class="button is-static is-unselectable">
|
||||
|
|
@ -268,6 +278,7 @@ const dialog_confirm = ref({
|
|||
message: '',
|
||||
options: [],
|
||||
})
|
||||
const FORCE_FLAG = '--no-download-archive'
|
||||
|
||||
const addDownload = async () => {
|
||||
if (form.value?.cli && '' !== form.value.cli) {
|
||||
|
|
@ -454,4 +465,26 @@ const get_output_template = () => {
|
|||
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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue