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

Add force download switch
This commit is contained in:
Abdulmohsen 2025-08-02 16:45:06 +03:00 committed by GitHub
commit c310ae9abc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 80 additions and 35 deletions

3
.vscode/launch.json vendored
View file

@ -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
View file

@ -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"
}
}
]
}

View file

@ -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` |

View file

@ -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

View file

@ -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."

View file

@ -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,

View file

@ -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>