1 (изменено: kvk-2019, 2019-11-11 05:19:24)

Тема: JS: Утечка памяти при получении оглавления папки с видеофайлами

Пишу на .js года с 2000, читаю ваш форум с 2008 примерно. Всегда самостоятельно справлялся с отладкой.  Но не в этот раз. Возможно потому, что перестал писать скрипты году в 2016, да и здоровье уже не то. Суть проблемы. Году в 2012 занимался оцифровкой видеокассет. Написал скрипт, который выводил оглавления DVD/BD с указанием продолжительности записей. И теперь использую этот скрипт, но для других целей. Скачиваю с сайтов телеканалов или записываю по IPTV интересные передачи, вырезаю рекламу и отправляю для просмотра на SmartTV. Если видео не досмотрено до конца, оставляю только хвост, обработав ffmpeg, перед именем оригинального видео добавляю восклицательный знак, чтобы его продолжительность не учитывалась при подсчёте и запускаю этот скрипт, чтобы получить снова оглавление папки, в которой расположен он и видеофайлы. Проблему заметил вчера, когда правил алгоритм переноса названия видео по словам. В результате решил, что перенос не нужен, хотя он и работает, определяется значением переменной textAreaLength. А над проблемой утечки продолжил биться. Безрезультатно. При каждом запуске скрипта теряется около 120 Мб невыгружаемой памяти. Можно было бы и не искать, просто чаще перезагружаться. Но интересно где ошибка. Windows 10 Pro x64 18362.418. Вроде бы рекурсия не влияет, обычный запуск и с вызовом ffmpeg даёт одинаковый результат по утечке. В цикле объекты не создаются. Вот сам скрипт Оглавление.js:

var str, WshShell = new ActiveXObject("WScript.Shell");
var textAreaLength = 158, headLength = 10, isArial = false; // isArial - нужно ли печатать на принтере крупно или только смотреть в Far Manager
var ffProbe = WshShell.ExpandEnvironmentStrings("%FFmpegPath%") + "ffprobe.exe"; tailLength = textAreaLength - headLength;
var re = new RegExp("\\s*(.{0," + (isArial ? 59 : tailLength) +"}[^-\\s])(?=\\s+|$|-)","g");
var fso = new ActiveXObject("Scripting.FileSystemObject"), shellApp = new ActiveXObject("Shell.Application"), header;
var s, sum, Arg, Args, startFolders = [], dd = (new Array(isArial ? 91 : textAreaLength)).join("-"), fsp = (new Array(isArial ? 16 : headLength)).join(" ");

with(str = new ActiveXObject("ADODB.Stream")){Type = 2; Mode = 3;}

if(ArgsCount=(Args=WSH.Arguments.Unnamed).Count){
	for(var i=0;i<ArgsCount;i++)if(fso.FolderExists(Args(i)))startFolders.push(fso.GetAbsolutePathName(Args(i)))
}else startFolders = [fso.GetParentFolderName(WSH.ScriptFullName)];

for(var curFolder=0;curFolder<startFolders.length;curFolder++){s = "" ; sum = [0,0,0];
	getInfo(folder=startFolders[curFolder]); normTime(sum); header = "Оглавление " + fso.GetFileName(folder);
	with(fso.CreateTextFile(fso.BuildPath(folder, header + ".txt"), true, true)){
			Write((isArial ? ">>>Arial 16<<<\r\n" : "") + header + " (" + sum.join(":")+ "):" + (s.substr(2,1) == "-" ? "" : "\r\n" + dd) + s);
			Close();
	}
}


function getInfo(folder){
	var sf = new Enumerator(fso.GetFolder(folder).subFolders), list = [], duration = new Array(3);
	for(;!sf.atEnd();sf.moveNext())list[list.length] = sf.item().Name;
    list = list.sort();
    for(var i=0;i<list.length;i++)
		if(/^(?:\d+[a-z]?)(?:[-,]\d+[a-z]?)*\./.test(list[i]))getInfo(fso.BuildPath(folder, list[i]));
	var cs = "", len = [0,0,0], sFolder = shellApp.NameSpace(folder);
	var fc = new Enumerator(fso.GetFolder(folder).files);
    list = [];
	for(;!fc.atEnd();fc.moveNext())list[list.length] = fc.item().Name;
    list = list.sort();
    for(var j=0;j<list.length;j++)
        if(/^(?:avi|mp4|mkv|ts)$/i.test(fso.GetExtensionName(fn=list[j])))
		if(/(?:Длина|Продолжительность):\s(\d{1,2}):(\d{2}):(\d{2})/.test(sFolder.GetDetailsOf(sFolder.ParseName(fn), -1)) ||
			(sFolder.GetDetailsOf(null, 27) == "Продолжительность" &&
				/(\d{1,2}):(\d{2}):(\d{2})/.test(sFolder.GetDetailsOf(sFolder.ParseName(fn), 27))) ||
                ffget(fso.BuildPath(folder, fn))){
			cs += "\r\n" + splitStrings(("0" + (duration[0] = RegExp.$1)).slice(-2) + ":" + (duration[1] = RegExp.$2) + ":" + (duration[2] = RegExp.$3) + " | " + fn, true);
            if(!/\.!|^!/.test(fn))for(var i=0;i<3;i++)len[i] += parseInt(duration[i], 10);
		}
	for(i=0;i<3;i++)sum[i] += len[i]; normTime(len); cf = folder.slice(startFolders[curFolder].length).replace(/^\\/,"");
	if(cs)s += "\r\n" + (cf ? splitStrings(cf + " ("+len.join(":") + "):") + "\r\n" : "") + dd + cs + "\r\n" + dd;
}


function splitStrings(inp, isFileName){var out = "", row = 0;
	while(re.exec(inp))out += "\r\n" + (isFileName && row++ ? fsp + "| " + RegExp.$1 : RegExp.$1);
	return out.substr(2);
}


function normTime(len){
	for(var i=2;i;i--){if(len[i]>=60){len[i-1] += Math.floor(len[i]/60); len[i] %= 60} len[i] = ("0" + len[i]).slice(-2)}
}

function ffget(fname){
    oExec = WshShell.Exec(ffProbe + ' -show_format -pretty "' + fname + '"');
    while(!oExec.Status || !oExec.StdOut.AtEndOfStream){
        if(/duration=(\d{1,2}):(\d{2}):(\d{2})/.test(DosToWin(oExec.StdOut.ReadLine())))return true;
    }
    return false;
}

function DosToWin(dosString){
    var result;
    with(str){
        Open();
        Charset = "Windows-1251";
        WriteText(dosString);
        Position = 0;
        Charset = "cp866";
        result = ReadText();
        Close();
    }
    return result;
}

2

Re: JS: Утечка памяти при получении оглавления папки с видеофайлами

Обнаружил утечку памяти в драйвере одной из систем безопасности. Связался с техподдержкой. Скрипт, возможно, вполне рабочий. Посмотрим, что ответят.

3

Re: JS: Утечка памяти при получении оглавления папки с видеофайлами

Сегодня ответили, что такое поведение предусмотрено "By design", подробно объяснили и предложили включить папку со скриптом и видеофайлами в исключения системы защиты. Сработало. Долго только выясняли. Но и на том спасибо им.