1

Тема: AHK: v2 loop files на яндекс-диске

Команда loop files dirPath "\*", "D"  неправильно отрабатывает на яндекс-диске. Папки, вложенные в папки первого уровня, то показываются, то нет. Сегодня они не показываются в одних папках, завтра - в других, но полностью - никогда. Помогите.

2

Re: AHK: v2 loop files на яндекс-диске

Это не проблема AHK. Вероятнее всего дело в том, что некоторые файлы/папки в папках облачных дисков могут быть видны, но фактически не находятся у вас на компьютере. Когда вы к ним обращаетесь, они скачиваются.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3

Re: AHK: v2 loop files на яндекс-диске

teadrinker
Ответ непонятен. Да, папки находятся на другом компьютере.  Да, к ним надо обратиться. Но разве команда loop files не делает именно это: обращается? Проводник Windows показывает все вложенные папки на подключённом яндекс-диске. Чем он лучше AHK?

4 (изменено: haridev, 2025-07-26 23:13:28)

Re: AHK: v2 loop files на яндекс-диске

Извините, возможно, я неправильно описал проблему. Вот код

Не всегда показываются вложенные папки при рекурсивном вызове процедуры BuildDirectoryTree

5

Re: AHK: v2 loop files на яндекс-диске

haridev пишет:

Но разве команда loop files не делает именно это: обращается? Проводник Windows показывает все вложенные папки на подключённом яндекс-диске. Чем он лучше AHK?

Нет, обращаться — значит пытаться прочитать. Проводник показывает все файлы, и которые на компьютере, и которые на облаке, а команда loop видит только которые на компьютере.

haridev пишет:

Вот код

Видимо, не получилось код правильно вписать, проверьте.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6 (изменено: haridev, 2025-07-26 23:05:20)

Re: AHK: v2 loop files на яндекс-диске

del

7

Re: AHK: v2 loop files на яндекс-диске

Оформите тегом code.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: v2 loop files на яндекс-диске

teadrinker
Сделал.

9

Re: AHK: v2 loop files на яндекс-диске

Если нужно показывать вложенные папки, нужно указать это в опциях (буква R — Recurse into subdirectories):

loop files, dirPath "\*", "DR"
{
    MsgBox A_LoopFilePath
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: v2 loop files на яндекс-диске

teadrinker
Порядок обхода по опции "R" не годится для решаемой задачи.

11

Re: AHK: v2 loop files на яндекс-диске

А на других папках (не на Яндекс диске) работает правильно?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: v2 loop files на яндекс-диске

Да, правильно.

13

Re: AHK: v2 loop files на яндекс-диске

Ну, значит с кодом всё в порядке. Каких-то специальных приёмов для работы с папками облачных дисков нет.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14

Re: AHK: v2 loop files на яндекс-диске

Кстати, код не совсем корректно работает. От некоторых папок возникают непонятные отростки, которые ничем не заканчиваются:
 
 https://i.ibb.co/W74g53G/2025-07-25-17-17-41-6.png

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15 (изменено: haridev, 2025-08-13 22:38:05)

Re: AHK: v2 loop files на яндекс-диске

del

16

Re: AHK: v2 loop files на яндекс-диске

Кстати, консольная команда Windows Tree также не работает нормально на яндекс-диске.

17

Re: AHK: v2 loop files на яндекс-диске

Вот алгоритм немного попроще (без подробностей):

#Requires AutoHotkey v2

MsgBox A_Clipboard := DirTreeText('D:\OneDrive\Scripts')

DirTreeText(dir, prefix := '', isLast := true, depth := 0, &out := '') {
    SplitPath dir, &name
    out .= (depth ? prefix . (isLast ? '└───' : '├───') : '') . name . '`n'

    dirs := []
    Loop files, dir '\*', 'D'
        dirs.Push(A_LoopFilePath)

    nextPrefix := depth ? prefix . (isLast ? '    ' : '│   ') : ''

    for idx, subDir in dirs
        DirTreeText(subDir, nextPrefix, idx = dirs.Length, 1, &out)

    return out
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

18

Re: AHK: v2 loop files на яндекс-диске

Подправил немного.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

19

Re: AHK: v2 loop files на яндекс-диске

teadrinker
Большое спасибо! Классный алгоритм. Утащил себе.

20

Re: AHK: v2 loop files на яндекс-диске

haridev, когда была задача сделать нечто подобное, но с гугл драйв, то использовал его апи.
Думаю, с яндексом ситуация такая же.

21

Re: AHK: v2 loop files на яндекс-диске

Malcev
Поделитесь опытом подробнее, пожалуйста.

22

Re: AHK: v2 loop files на яндекс-диске

Задаете этот вопрос чат жпт, он вам дает пример rest api yandex.
Вызываете эти get, post запросы на ахк.
В принципе чат жпт и код может готовый вам на автохотки состряпать.

23 (изменено: haridev, 2025-09-19 12:56:29)

Re: AHK: v2 loop files на яндекс-диске

Malcev
Спасибо, попробую. Пока нашёл другое решение: обход яндекс-диска с задержкой. 400 мс вроде хватает.

/*
Программа для создания каталога в виде дерева папок, начиная с выбранной.
Сетевые диски ("Network") обходятся с подобранной (и, возможно, не всегда достоточной) задержкой.
Для яндекс-дисков лучше пользоваться скриптом YDPlane или YDTree.
teadrinker, Thanks a lot!
*/
#Requires AutoHotkey v2
; Функция для рекурсивного обхода директории и построения дерева
DirTreeText(dir, prefix, isLast := true, depth := 0) {
	global tree, info
	static FoldersCount := 0, Network := substr(prefix, 1, 4) = 'Net_'

	dirs := []
    Loop files dir '\*', 'D'
        dirs.Push A_LoopFilePath

	if depth {
		SplitPath dir, &name
		Time := ''
		try Time := FormatTime(FileGetTime(dir, 'C'), 'dd.MM.yy HH:mm')
		tree .= prefix (isLast ? '└───' : '├───') name A_Tab A_Tab Time '`n'
		Info['Status'].SetText('Папка № ' . ++FoldersCount, 1), Info['Status'].SetText(name, 2)
		prefix := prefix (isLast ? '    ' : '│   ')
	} else	Info['Progress'].Opt('Range0-' dirs.Length)

    for idx, subDir in dirs {
		if !depth
			Info['Progress'].Value++
		if Network
			sleep 400
        DirTreeText subDir, prefix, idx = dirs.Length, 1
	}
}

; Основная часть программы
if NOT startFolder :=  A_Args.Length ? A_Args[1] : RegExReplace(DirSelect(, 0, 'Выберите начальную папку для построения дерева'), '\\$')
	ExitApp
SplitPath startFolder,,,,, &Drive
if DriveGetType(Drive) = 'Network' {
	DriveLabel := 'Net_' substr(Drive, 1, -1)
	SpaceFree := ''
} else {
	DriveLabel := DriveGetLabel(Drive)
	SpaceFree := DriveGetSpaceFree(Drive)//1024  ' GiB свободно из ' DriveGetCapacity(Drive)//1024
}
outputFile := A_ScriptDir '\' DriveLabel '.txt'
if NOT A_Args.Length and NOT outputFile := FileSelect('S16', outputFile, 'Файл для каталога', 'Documents (*.txt)')
	ExitApp
global tree := 'Том: ' DriveLabel ', '  SpaceFree '`n' startFolder '`n'

; информационное окно
BarWidth := 300
global Info := Gui("+LastFound", 'Info - ' A_ScriptName)
Info.AddText 'vText1 r2 w' BarWidth, 'Построение дерева...'
Info.AddProgress 'vProgress h10 w' BarWidth
Info.AddButton 'vButton Default w' BarWidth, 'Cancel'
Info['Button'].OnEvent('Click', (*) => ExitApp())
Info.AddStatusBar 'vStatus'
Info['Status'].SetParts(85)
Info.Show 'w' BarWidth+Info.MarginX*2

; Построение дерева
Time1 := A_TickCount
DirTreeText startFolder, DriveLabel
statistics := 'Затрачено ' (A_TickCount - Time1) // 1000 ' с'
tree .= '`n' statistics '`n'

; Сохраняем в файл
try	FileDelete outputFile
try FileAppend tree, outputFile, 'UTF-8'
catch as e {
	MsgBox 'Ошибка при записи в файл: ' e.Message
	ExitApp
}

Info['Text1'].Text := statistics '. Сохранено в `n' outputFile
Info['Button'].Text := 'OK'
Info['Button'].GetPos(&X, &Y, &Width, &Height)
WinActivate
MouseMove X + Width // 2, Y + Height // 2
SoundPlay "*-1"

24 (изменено: haridev, 2025-08-24 07:06:36)

Re: AHK: v2 loop files на яндекс-диске

Приступил к rest api яндекс. Ввожу строку для curl с полигона и получаю ошибки. Может, кто подскажет, в чём дело?

D:\Мои документы\AutoHotkey>curl --version
curl 8.15.0 (x86_64-w64-mingw32) libcurl/8.15.0 OpenSSL/3.5.1 zlib/1.3.1.zlib-ng libidn2/2.3.8 libpsl/0.21.5
Release-Date: 2025-07-16
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtsp smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS HSTS HTTPS-proxy IDN IPv6 Largefile libz PSL SSL threadsafe TLS-SRP Unicode UnixSockets

D:\Мои документы\AutoHotkey>curl -X GET --header 'Accept: application/json' --header 'Authorization: OAuth <токен> 'https://cloud-api.yandex.net/v1/disk/resources?path=%2F'
curl: (6) Could not resolve host: application
curl: (6) Could not resolve host: OAuth
curl: (3) URL rejected: Bad hostname
curl: (3) URL rejected: Port number was not a decimal number between 0 and 65535

Проблему решил заменой ' на " )))

25 (изменено: haridev, 2025-08-30 23:28:09)

Re: AHK: v2 loop files на яндекс-диске

Ну вот, сделал на rest api яндекс. Получилось медленнее больше чем на минуту. Не думаю, что будет быстрее не через curl, а непосредственно через библиотеку.

26

Re: AHK: v2 loop files на яндекс-диске

В AHK для веб-запросов не нужно использовать сторонние приложения. Смотрите примеры с ком-объектом WinHttp.WinHttpRequest.5.1.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

27 (изменено: haridev, 2025-08-30 22:29:16)

Re: AHK: v2 loop files на яндекс-диске

Да, с WinHttp получилось даже быстрее, чем в варианте с подключённым диском: 187 с против 194 )))

Если что, я пенсионер, вне программистского сообщества с 1988 г. Последний рабочий язык - visual foxpro. Для меня многое в новинку, буду премного благодарен за подсказки.

28

Re: AHK: v2 loop files на яндекс-диске

Насколько я понял по сообществу форума на autohotkey.com, среди пользователей AHK многие уже в зрелом возрасте. Приятно, что люди выбирают программирование в качестве хобби, а не сидение на скамейке во дворе.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

29

Re: AHK: v2 loop files на яндекс-диске

Программировать - приятно! ))

30 (изменено: haridev, 2025-08-30 22:28:21)

Re: AHK: v2 loop files на яндекс-диске

С LightJson стало ещё на несколько секунд быстрее

Есть недочёт. При ошибке WinHttp.WinHttpRequest.5.1 в ответе показывает тарабарщину:
{"error":"NotFoundError","description":"Not Found","message":"? ?µN?N?N?N? ???µ ???°?????µ??."}
Можно с этим что-то сделать?

31

Re: AHK: v2 loop files на яндекс-диске

ResponseText не поддерживает юникодные символы. Чтобы избежать кракозябров, нужно читать данные из свойства responseBody, возвращающего бинарный массив, который потом можно интерпретировать, как строку в кодировке UTF-8. Для простых случаев я использую такую функцию, где это реализовано:

WebRequest(url, method := 'GET', headersMap := '', retBinary := false, body?, &status?) {
    Whr := ComObject('WinHttp.WinHttpRequest.5.1')
    Whr.Open(method, url, true)
    if headersMap is Map {
        for name, value in headersMap
            Whr.SetRequestHeader(name, value)
    }
    Whr.Send(body ?? '')
    Whr.WaitForResponse()
    status := Whr.status
    if SafeArray := Whr.responseBody {
        pData := NumGet(ComObjValue(SafeArray) + 8 + A_PtrSize, 'Ptr')
        length := SafeArray.MaxIndex() + 1
        if !retBinary {
            return StrGet(pData, length, 'UTF-8')
        }
        DllCall('RtlMoveMemory', 'Ptr', data := Buffer(length), 'Ptr', pData, 'Ptr', length)
        return data
    }
    return ''
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

32 (изменено: haridev, 2025-10-01 20:19:18)

Re: AHK: v2 loop files на яндекс-диске

Зря грешил на rest api яндекс, на мне грех. В функции URIEncode было "%{:X}" вместо правильного "%{:02X}". Сейчас что не работало - работает.

33

Re: AHK: v2 loop files на яндекс-диске

Кодировать URL надежнее не с помощью ручного парсинга, а с помощью специального API. Например:

#Requires AutoHotkey v2.0

encoded1 := UrlCodec.Encode('https://forum.script-coding.com/Привет')
encoded2 := UrlCodec.Encode('https://forum.script-coding.com/Привет', false)
decoded1 := UrlCodec.Decode(encoded1)
decoded2 := UrlCodec.Decode(encoded2)

MsgBox encoded1 . '`n`n'
     . encoded2 . '`n`n'
     . decoded1 . '`n'
     . decoded2

class UrlCodec
{
    static Encode(str, segment := true) {
        static E_POINTER := 0x80004003
             , URL_ESCAPE_PERCENT             := 0x00001000
             , URL_ESCAPE_SEGMENT_ONLY        := 0x00002000
             , URL_ESCAPE_AS_UTF8             := 0x00040000
             , URL_ESCAPE_ASCII_URI_COMPONENT := 0x00080000

        flags := ( URL_ESCAPE_PERCENT | URL_ESCAPE_ASCII_URI_COMPONENT
                 | URL_ESCAPE_AS_UTF8 | (segment ? URL_ESCAPE_SEGMENT_ONLY : 0) )

        req := StrLen(str)
        Loop 2 {
            buf := Buffer(req * 2)
            r := DllCall('Shlwapi\UrlEscape', 'Str', str, 'Ptr', buf, 'UInt*', &req, 'UInt', flags, 'UInt')
            if !(r = E_POINTER || r = 0) {
                throw Error('UrlEscape failed, error: ' . Format('{:#x}', r))
            }
        } until r == 0
        return StrGet(buf)
    }

    static Decode(encoded) {
        static E_POINTER := 0x80004003, flags := (URL_UNESCAPE_AS_UTF8 := 0x00040000)
        req := StrLen(encoded)
        Loop 2 {
            buf := Buffer(req * 2)
            r := DllCall('Shlwapi\UrlUnescape', 'Str', encoded, 'Ptr', buf, 'UInt*', &req, 'UInt', flags, 'UInt')
            if !(r = E_POINTER || r = 0) {
                throw Error('UrlUnescape failed, error: ' . Format('{:#x}', r))
            }
        } until r == 0
        return StrGet(buf)
    }
}

Или так:

class UrlCodec
{
    static __New() {
        static document := ''
        document := ComObject('htmlfile')
        document.write('<meta http-equiv="X-UA-Compatible" content="IE=9">')
        this.JS := document.parentWindow
        (document.documentMode < 9 && this.JS.execScript())
    }
    static Encode(str, component := true) => this.JS.%'encodeURI' . (component ? 'Component' : '')%(str)
    static Decode(str, component := true) => this.JS.%'decodeURI' . (component ? 'Component' : '')%(str)
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

34 (изменено: haridev, 2025-09-08 12:01:21)

Re: AHK: v2 loop files на яндекс-диске

Выбрал вариант JS, он побыстрее

35 (изменено: haridev, 2025-09-08 11:54:44)

Re: AHK: v2 loop files на яндекс-диске

Быстрый вариант для своего яндекс-диска
Del

36 (изменено: haridev, 2025-09-20 22:25:48)

Re: AHK: v2 loop files на яндекс-диске

Быстрый вариант с датами

; для создания каталога своего яндекс-диска из плоского списка
; с датами по самому раннему файлу в папке, без пустых папок
; токен должен быть в файле YandexToken.txt, рядом со скриптом

#Requires AutoHotkey v2
#DllLoad 'WinHttp.dll'
#Include <LightJson>	; https://forum.script-coding.com/viewtopic.php?id=18483
#Include <WebRequest>	; https://forum.script-coding.com/viewtopic.php?pid=162651#p162651
#Include <UrlCodec>		; https://forum.script-coding.com/viewtopic.php?pid=162669#p162669

; сортировка вставками из Википедии
InsSortAMap(A, item := '', lo := 1, hi?) {
	if !IsSet(hi)
		hi := A.Length
	j := lo + 1
	while j <= hi {
		Aj := A[j]
		pivot:= item ? Aj[item] : Aj
		i := j - 1
		while i >= lo && StrCompare(item ? A[i][item] : A[i], pivot, 'On') > 0
			A[i + 1] := A[i--]
		A[i+1] := Aj
		j++
	}
}

; сортировка по схеме Хоара из Википедии, модифицированная
qSortAMap(A, item := '', lo := 1, hi?, threshold := 15, depth := 0) {

	static partition(A, item, low, high) {
		pivot:= item ? A[(low + high) >> 1][item] : A[(low + high) >> 1]
		i := low
		j := high
		loop {
			while StrCompare(item ? A[i][item] : A[i], pivot, 'On') < 0
              i++
			while StrCompare(item ? A[j][item] : A[j], pivot, 'On') > 0
              j--
			if i >= j
				return j
			; swap
			temp := A[i], A[i] := A[j], A[j] := temp
			i++, j--
		}
	}

	if !IsSet(hi)
		hi := A.Length
	while hi - lo > threshold {
		p:= partition(A, item, lo, hi)
		if p - lo < hi - p {
			qSortAMap(A, item, lo, p, threshold, 1)
			lo := p + 1
		} else {
			qSortAMap(A, item, p + 1, hi, threshold, 1)
			hi := p
		}
	}

	if !depth
		InsSortAMap A, item

}

; найти в отсортированном массиве 1-ю строку с указанным началом
SASearch(A, &str, item := '') {
	if !j := A.Length
		return 0
	i := 1, L := StrLen(str)
	loop {
		m := i + ((j - i) >> 1)
		if StrCompare(str, substr(item ? A[m][item] : A[m], 1, L), 'On') <= 0
			j := m - 1
		else i := m + 1
	} until i > j
	return  ++j <= A.Length && StrCompare(str, substr(item ? A[j][item] : A[j], 1, L), 'On') = 0 ? j : 0
}

GetYD(req, &Response, &status, &dir?, &token?, limit := 1000, offset := 0) {	; запрос к облаку
	url := 'https://cloud-api.yandex.net/v1/disk' . (req = 'p_res' || req = 'p_nam' ? '/public/resources?public_key=' . token : '')
	if req = 'res' || req = 'p_res'
		P := '_embedded.items.', fields := 'fields=' P 'type%2C,' P 'path%2C,' P 'name%2C,' P 'created'
	switch {
	case req = 'res'	: url .= '/resources?' fields '&limit=' limit '&path=' UrlCodec.Encode(dir)
	case req = 'disk'	: url .= '?fields=total_space%2Cused_space'
	case req = 'p_res'	: url .= '&' fields '&limit=' limit '&path=' UrlCodec.Encode(dir)
	case req = 'p_nam'	: url .= '&fields=name'
	case req = 'plane'	: url .= '/resources/files?limit=' limit '&offset=' offset '&fields=items.path%2Citems.created'
	Default:
		Response := 'Неверный запрос', status := -1
		return 0
	}
	headers := Map("Accept", "application/json")
	if instr('res disk plane', req)
		headers["Authorization"] := token
	Response := WebRequest(url,, headers,,, &status)
	return status = 200
}

; Функция для рекурсивного обхода списка директорий и построения дерева
DirTreeText(dir, prefix, isLast := true, depth := 0, created := '') {
	global tree, info, List
	static UTCPlus := DateDiff(A_Now, A_NowUTC, 'H')

	if 	NOT idx := SASearch(List, &dir, 'path') {
		MsgBox 'Не найдена папка`n' dir
		ExitApp
	}
	; список папок, вложенных в папку dir
	dirs := []
	while idx <= List.Length && substr(List[idx]['path'], 1, strlen(dir)) = dir {
		if subDirEndPos := InStr(List[idx]['path'], '/', 'On', strlen(dir) + 1) {
			; subDirEndPos = 0 для папки dir, поэтому она не попадёт в dirs
			path := SubStr(List[idx]['path'], 1, subDirEndPos)
			if !SASearch(dirs, &path, 'path')
				dirs.Push Map('path', path
				, 'created', FormatTime(DateAdd(RegExReplace(substr(List[idx]['created'], 1, -6), '-|T|:'), UTCPlus, 'H'), 'dd.MM.yy HH:mm'))
		}
		idx++
	}

	if depth {
		namePos := instr(dir, '/', 'On',, -2)
		currentFolder := SubStr(dir, namePos+1, StrLen(dir)-NamePos-1)
		tree .= prefix (isLast ? '└───' : '├───') currentFolder '`t`t' created '`n'
		prefix := prefix (isLast ? '    ' : '│   ')
	}

    for idx, subDir in dirs
        DirTreeText subDir['path'], prefix, idx = dirs.Length, 1, subDir['created']
}

FileName := A_ScriptDir '\YandexToken.txt'
try
    FileObj := FileOpen(FileName, "r")
catch as Err {
    MsgBox "Не читается файл с токеном `n" FileName
        . "`n`n" Type(Err) ": " Err.Message
    ExitApp
}
token := "OAuth " rtrim(FileObj.Read(), '`r`n')
FileObj.Close()

startFolder := '/'
if !GetYD('disk', &Response, &status,, &token) {
	MsgBox 'Ошибка ' status ' на информации о диске`n' Response
	ExitApp
}

DiskInfo := LightJson.Parse(Response)
SpaceFree := (Integer(DiskInfo['total_space'])-Integer(DiskInfo['used_space']))>>>20 ' GiB свободно из ' Integer(DiskInfo['total_space'])>>>20
DriveLabel := 'YD'

outputFile := A_ScriptDir '\' DriveLabel '.txt'

; информационное окно
BarWidth := 300
global Info := Gui("+LastFound", 'Info - ' A_ScriptName)
Info.AddText 'vText1 r2 w' BarWidth, 'Построение дерева...'
Info.AddButton 'vButton Default w' BarWidth, 'Cancel'
Info['Button'].OnEvent('Click', (*) => ExitApp())
Info.AddStatusBar 'vStatus'
Info.Show 'w' BarWidth+Info.MarginX*2
SetTimer Wink, 1000
Wink() {
	static ColorInd := 1, Colour := ['cDefault', 'cWhite'], Period := [1000, 500]
	SetTimer , Period[ColorInd+1]
	Info['Text1'].Opt(Colour[ColorInd+1])
	ColorInd := !ColorInd
}

Time1 := A_TickCount
limit := 3000, offset := 0
global List := []

loop {
	Info['Status'].SetText('Запрос, offset=' offset)
	if !GetYD('plane', &Response, &status,, &token, limit, offset) {
		MsgBox 'Ошибка ' status ' на получении списка`n' Response
		ExitApp
	}
	Info['Status'].SetText('Обработка, offset=' offset)
	Lst := LightJson.Parse(Response)['items']

	for idx, Item in Lst ; убираем имена файлов и префикс disk:
		Lst[idx]['path'] := SubStr(Item['path'], 6, instr(Item['path'], '/', 'On', -1) - 5)

	qSortAMap Lst, 'path'

	idx := 1
	while idx <= Lst.Length { ;  неодинаковые добавляем в List
		if substr(Lst[idx]['path'], 1, StrLen(startFolder)) != startFolder {
			idx++
			continue ; ненужные пропускаем
		}
		List.Push Lst[idx]
		path := Lst[idx]['path']
		while ++idx <= Lst.Length && Lst[idx]['path'] = path { ; одинаковые пропускаем, корректируя дату
			if StrCompare(Lst[idx]['created'], List[List.Length]['created'], 'On') < 0
				List[List.Length]['created'] := Lst[idx]['created']
		}
	}
	offset += limit
} until lst.Length < limit

if offset > limit { ; удаляем одинаковые из List, корректируя дату
	Info['Status'].SetText('Обработка...')
	qSortAMap List, 'path'
	idx := 2
	while idx <= List.Length
		if List[idx]['path'] = List[idx-1]['path'] {
			if StrCompare(List[idx]['created'], List[idx-1]['created'], 'On') < 0
				List[idx-1]['created'] := List[idx]['created']
			List.RemoveAt(idx)
		} else idx++
}

; теперь строим дерево
Info['Status'].SetText('Строим...')
global tree := DriveLabel ', '  SpaceFree '`n' startFolder '`n'
DirTreeText startFolder, DriveLabel
statistics := 'Затрачено ' (A_TickCount - Time1) // 1000 ' с'
tree .= '`n' statistics '`n'

; Сохраняем в файл
try	FileDelete outputFile
try FileAppend tree, outputFile, 'UTF-8'
catch as e {
	MsgBox 'Ошибка при записи в файл: ' e.Message
	ExitApp
}

SetTimer Wink, 0
Info['Text1'].Opt('cDefault')
Info['Text1'].Text := statistics '. Сохранено в `n' outputFile
Info['Status'].SetText('Готово')
Info['Button'].Text := 'OK'
Info['Button'].GetPos(&X, &Y, &Width, &Height)
WinActivate
MouseMove X + Width // 2, Y + Height // 2
SoundPlay "*-1"

Небыстрый вариант

; для создания каталога публичного ресурса на яндекс-диске или каталога своего яндекс-диска
; если на запрос публичного URL вводится пустое значение, токен берётся из файла YandexToken.txt, расположенного рядом со скриптом
#Requires AutoHotkey v2
#DllLoad 'WinHttp.dll'
#Include <LightJson>	; https://forum.script-coding.com/viewtopic.php?id=18483
#Include <WebRequest>	; https://forum.script-coding.com/viewtopic.php?pid=162651#p162651
#Include <UrlCodec>		; https://forum.script-coding.com/viewtopic.php?pid=162669#p162669

GetYD(req, &Response, &status, &dir?, &token?, limit := 1000, offset := 0) {	; запрос к облаку
	url := 'https://cloud-api.yandex.net/v1/disk' . (req = 'p_res' || req = 'p_nam' ? '/public/resources?public_key=' . token : '')
	if req = 'res' || req = 'p_res'
		P := '_embedded.items.', fields := 'fields=' P 'type%2C,' P 'path%2C,' P 'name%2C,' P 'created'
	switch {
	case req = 'res'	: url .= '/resources?' fields '&limit=' limit '&path=' UrlCodec.Encode(dir)
	case req = 'disk'	: url .= '?fields=total_space%2Cused_space'
	case req = 'p_res'	: url .= '&' fields '&limit=' limit '&path=' UrlCodec.Encode(dir)
	case req = 'p_nam'	: url .= '&fields=name'
	case req = 'plane'	: url .= '/resources/files?limit=' limit '&offset=' offset '&fields=items.path%2Citems.created'
	Default:
		Response := 'Неверный запрос', status := -1
		return 0
	}
	headers := Map("Accept", "application/json")
	if instr('res disk plane', req)
		headers["Authorization"] := token
	Response := WebRequest(url,, headers,,, &status)
	return status = 200
}

; Функция для рекурсивного обхода директории и построения дерева
DirTreeText(dir, prefix, isLast := true, depth := 0, name := '', created := '') {
	global tree, info, token
	static FoldersCount := 0, AlwaysContinue := false, UTCPlus := DateDiff(A_Now, A_NowUTC, 'H'), req := substr(token, 1, 6) = "OAuth " ? 'res' : 'p_res'

	dirs := []
	if GetYD(req, &Response, &status, &dir, &token) {
		for idx, Item in LightJson.Parse(Response)["_embedded"]["items"]
			if Item['type'] = 'dir'
				dirs.Push Map('path', Item['path'], 'name', Item['name']
				, 'created', FormatTime(DateAdd(RegExReplace(substr(Item['created'], 1, -6), '-|T|:'), UTCPlus, 'H'), 'dd.MM.yy HH:mm'))
	}
	else {
		if !AlwaysContinue {
			WhatToDo := MsgBox('Ошибка ' status ' на папке `n' dir '`n' Response '`n`n' "
			(
			Да`t- продолжить и больше не спрашивать;
			Нет`t- продолжить до следующей ошибки;
			Отмена`t- прекратить.
			)",, 'YNC Icon?')
			if WhatToDo = 'Cancel'
				ExitApp
			AlwaysContinue := WhatToDo = 'Yes'
		}
		name := dir '`n' UrlCodec.Encode(dir)
		created := 'ошибка'
	}
	if depth {
		tree .= prefix (isLast ? '└───' : '├───') name '`t`t' created '`n'
		Info['Status'].SetText('Папка № ' . ++FoldersCount, 1), Info['Status'].SetText(name, 2)
		prefix := prefix (isLast ? '    ' : '│   ')
	} else	Info['Progress'].Opt('Range0-' dirs.Length)

    for idx, subDir in dirs {
		if !depth
			Info['Progress'].Value++
        DirTreeText subDir['path'], prefix, idx = dirs.Length, 1, subDir['name'], subDir['created']
	}
}

startFolder := '/'
IB := InputBox("Публичный URL",,'h100')
if IB.Result = "Cancel"
    ExitApp
global token
if token := IB.Value {
	if !GetYD('p_nam', &Response, &status,, &token) {
		MsgBox 'Ошибка ' status ' на информации о публичном имени`n' Response
		ExitApp
	}
	DriveLabel := 'YD-' LightJson.Parse(Response)['name']
	SpaceFree := ''
	IB := InputBox("Имя выходного файла",,'h100', DriveLabel)
	if IB.Result = "Cancel"
		ExitApp
	DriveLabel := IB.Value
} else {
	FileName := A_ScriptDir '\YandexToken.txt'
	try
		FileObj := FileOpen(FileName, "r")
	catch as Err {
		MsgBox "Не читается файл с токеном `n" FileName
        . "`n`n" Type(Err) ": " Err.Message
		ExitApp
	}
	token := "OAuth " rtrim(FileObj.Read(), '`r`n')
	FileObj.Close()
	if !GetYD('disk', &Response, &status,, &token) {
		MsgBox 'Ошибка ' status ' на информации о диске`n' Response
		ExitApp
	}
	DiskInfo := LightJson.Parse(Response)
	SpaceFree := (Integer(DiskInfo['total_space'])-Integer(DiskInfo['used_space']))>>20 ' GiB свободно из ' Integer(DiskInfo['total_space'])>>20
	DriveLabel := 'YD'
}

outputFile := A_ScriptDir '\' DriveLabel '.txt'

; информационное окно
BarWidth := 300
global Info := Gui("+LastFound", 'Info - ' A_ScriptName)
Info.AddText 'vText1 r2 w' BarWidth, 'Построение дерева...'
Info.AddProgress 'vProgress h10 w' BarWidth
Info.AddButton 'vButton Default w' BarWidth, 'Cancel'
Info['Button'].OnEvent('Click', (*) => ExitApp())
Info.AddStatusBar 'vStatus'
Info['Status'].SetParts(85)
Info.Show 'w' BarWidth+Info.MarginX*2

; Построение дерева
global tree := DriveLabel ', '  SpaceFree '`n' startFolder '`n'
Time1 := A_TickCount
DirTreeText startFolder, DriveLabel
statistics := 'Затрачено ' (A_TickCount - Time1) // 1000 ' с'
tree .= '`n' statistics '`n'

; Сохраняем в файл
try	FileDelete outputFile
try FileAppend tree, outputFile, 'UTF-8'
catch as e {
	MsgBox 'Ошибка при записи в файл: ' e.Message
	ExitApp
}

Info['Text1'].Text := statistics '. Сохранено в `n' outputFile
Info['Button'].Text := 'OK'
Info['Button'].GetPos(&X, &Y, &Width, &Height)
WinActivate
MouseMove X + Width // 2, Y + Height // 2
SoundPlay "*-1"