OJBakker wrote: ↑2023-Mar-13, 12:07 pm
No need to use external tools.
Thank you for the reply. I actually found this solution (using quotes as a deliminator) through a Google search the other day, and finished my script. Well, "finished" in the sense that it works. Now I'm going to polish it and add some options.
For those who are interested;
Short version: It's a script to automatically fix broken NZB files generated by the NZBKing website.
Long version: NZB files are XML files used to download files from Usenet Newsgroups, which are decentralized message forums. The NZB file is kind of like a torrent in that it tells the client program what files to download. NZB files can, and usually do include multiple related files grouped together. In addition, each file is further broken up into parts to avoid hitting post size limits. The NZB lists all these parts so that the original file(s) can be recreated.
NZB search sites like NZBKing let you search for what you want and generate an NZB for it. Unfortunately, NZBKing creates broken files. It fails to append the part count to the end of the header for each file, such as (1/25). Because of this, most programs think any files with more than one part are broken. The solution is to check the number of parts for each file and put that information in the header for the file, which is a pain in the butt to do manually.
All header lines start with
<file poster= and the line below the last part in each set contains
</segments>. So my script uses Grep to count the number of files contained in the NZB, then uses a For loop to grep each matching header line and put the line number into a numbered array. Then it does the same for the /segments tags, subtracting one to get the line above it. SED is used to feed that line to For, which gets the the part number, then SED is used to append that number to the corresponding header line, writing it to a temporary file, the original is deleted, the temp file is renamed to the original name, it checks to see if it's hit the last file (in the NZB) yet, and if not, it loops, incrementing the counter.
Limitations: Technically, the parts can be listed in any order, so the last one listed isn't always guaranteed to be the last part of each file. However NZBKing always seems to list the parts in ascending order, and since the script is only needed for NZBs from that website, it shouldn't be a problem. Files in the newsgroups sometimes have missing parts and people post "Par" files to fix this. If the last part of a file is missing, it won't be listed in the NZB, but since the header doesn't tell you how many parts are in the file (the problem this script was created to fix), there's no way to know that the last part listed isn't the true last part. That should be a rare occurrence and shouldn't really make a difference as to how possible it is to download the files. If the part is missing, making the part count one number higher wouldn't change much.
It's not especially fast, and surprisingly, when I removed the array usage and instead dumped the Grepped line numbers to a temporary file and then used For with SED to pull just the appropriate lines out of that file and put them into variables, it was twice as slow! I figured that since it wouldn't have to jump around with a bunch of Calls, it would be faster, but that's not the case.
If you want to try it, search for anything you want on the NZBKing website, and save one or more NZB files, then run this script in that directory and it will fix them. The original files will be copied to a directory called Originals, and it will skip any files that have already been fixed (or that don't need fixing).
Code: Select all
@echo off
for %%F in (*.nzb) do (set Filename=%%~nF
call :ProcessNZB)
goto:eof
:ProcessNZB
for /f %%E in ('grep -c "<file poster=" "%Filename%.nzb"') do set FilesCount=%%E
for /f %%E in ('grep -c "yEnc (1/" "%Filename%.nzb"') do set FixedCount=%%E
if %FixedCount% equ %FilesCount% exit /b
md Originals 2>nul
echo %Filename%
copy "%Filename%.nzb" Originals\ >nul
set Counter=1
for /f "tokens=1 delims=:" %%E in ('grep -no "<file poster=" "%Filename%.nzb"') do (set Value=%%E
set Placeholder=Subject
call :ArrayIn
call :IncrementCounter)
set Counter=1
for /f "tokens=1 delims=:" %%E in ('grep -no "</segments>" "%Filename%.nzb"') do (set /a Value=%%E-1
set Placeholder=LastPart
call :ArrayIn
call :IncrementCounter)
set Counter=1
:FixNZB
set Placeholder=Subject
call :ArrayOut
set SubjectLine=%Value%
set Placeholder=LastPart
call :ArrayOut
set LastPartLine=%Value%
call :IncrementCounter
for /F tokens^=4^ delims^=^" %%P in ('sed -n "%LastPartLine%,%LastPartLine%p" "%Filename%.nzb"') do set LastPartNumber=%%P
sed "%SubjectLine%s/yEnc /yEnc (1\/%LastPartNumber%)/" "%Filename%.nzb" >Temp.tmp
del "%Filename%.nzb"
ren Temp.tmp "%Filename%.nzb"
if %Counter% leq %FilesCount% goto FixNZb
exit /b
:ArrayIn
call :Array2 %Placeholder%[%Counter%] %Value%
exit /b
:ArrayOut
call :Array2 Value %%%Placeholder%[%Counter%]%%
exit /b
:Array2
set %1=%2
exit /b
:IncrementCounter
set /a Counter=Counter+1
exit /b
And here's the slower version that doesn't use arrays;
Code: Select all
@echo off
for %%F in (*.nzb) do (set Filename=%%~nF
call :ProcessNZB)
del LineNumbers.tmp
goto:eof
:ProcessNZB
for /f %%E in ('grep -c "<file poster=" "%Filename%.nzb"') do set FilesCount=%%E
for /f %%E in ('grep -c "yEnc (1/" "%Filename%.nzb"') do set FixedCount=%%E
if %FixedCount% equ %FilesCount% exit /b
md Originals 2>nul
echo %Filename%
copy "%Filename%.nzb" Originals\ >nul
grep -no "<file poster=" "%Filename%.nzb" >LineNumbers.tmp
grep -no "</segments>" "%Filename%.nzb" >>LineNumbers.tmp
set Counter=1
set /a FilesCount=FilesCount+1
:FixNZB
set /a Counter2=Counter+FilesCount-1
for /f "tokens=1 delims=:" %%E in ('sed -n "%Counter%,%Counter%p" LineNumbers.tmp') do set SubjectLine=%%E
for /f "tokens=1 delims=:" %%E in ('sed -n "%Counter2%,%Counter2%p" LineNumbers.tmp') do set /a LastPartLine=%%E-1
for /F tokens^=4^ delims^=^" %%P in ('sed -n "%LastPartLine%,%LastPartLine%p" "%Filename%.nzb"') do set LastPartNumber=%%P
sed "%SubjectLine%s/yEnc /yEnc (1\/%LastPartNumber%)/" "%Filename%.nzb" >Temp.tmp
del "%Filename%.nzb"
ren Temp.tmp "%Filename%.nzb"
call :IncrementCounter
if %Counter% lss %FilesCount% goto FixNZb
exit /b
:IncrementCounter
set /a Counter=Counter+1
exit /b