fmang / opustags

Ogg Opus tags editor
BSD 3-Clause "New" or "Revised" License
75 stars 10 forks source link

feature request: support globs for -d #67

Closed vargobj closed 4 months ago

vargobj commented 5 months ago

usecase: When reindexing an opus file (i.e., an opus file that already has a time/chapter name index), it appears that the only way to remove the index is to iterate through each of those chapter tags (which must be known) and deleted using -d:

#!/bin/bash

title=Title\ for\ Testing\ --\ Audiobook.opus
tags=( $(opustags "$title") )
tags2=( $(printf '%s\n' "${tags[@]%=*}"|grep CHAPTER|sort -u) )
printf "%s\n" "${tags2[@]}"
for i in "${tags2[@]}"
  do
    opustags -i -d "$i" "$title"
  done

mediainfo "$title"

exit

$ opustags -d CHAPTER* appears to me to be far simpler than the above loop.

vargobj commented 5 months ago

The time per tag for removal is also quite high: 48-part book took 4:20 m:ss, 4 minutes of that being the chapter tag deletion.

Would it make more sense to do something like

⋮
opustags -i -d "$i1"  -d "$i2" ... -d "$in" "$title"
⋮
zvezdochiot commented 5 months ago

@vargobj , why delete chapters from a book? X(

vargobj commented 5 months ago

In order to reindex a book, the old index must be removed, otherwise all the old CHAPTER### information will be there along with the new information.

⋮
if [[ "$(opustags "$title")" = *CHAPTER* ]]
  then
    tags=( $(opustags "$title") )
    tags=( $(printf -- '-d%s\n' "${tags[@]%=*}"|\grep CHAPTER|sort -u) )
    opustags "${tags[@]}" -i "$title"
    ck4tags=( "$(opustags "$title" | grep --color=always -i chapter)" ) &&
    printf 'Error!\nCHAPTER### tags still present in opus file.\n%s\nexit 1' "${ck4tags[@]}" && exit 1
  else
    printf 'Mediainfo of existing %s:\n' "$title"
    mediainfo "$title"
fi
⋮

Obviously, if would be easier if I could just opustags -i -dCHAPTER* $title || exit 1

zvezdochiot commented 5 months ago

@vargobj

opustags -D

or

opustags -S

;)

fmang commented 5 months ago

Sounds like a good proposal. By replacing the strncasecmp with fnmatch we could get a working solution. We must ensure however that is not a valid character in tag names or, if it is, that there exists a way to escape the to match it literally. Alternatively, we could add an optional value to --delete-all and write -D=CHAPTER*. That latter option might be less surprising because it is plural.

As zvezdochiot suggested, you can use --set-all to replace all the tags at once. In your case, assuming they all fit in one line :

opustags file.opus | grep -v ^CHAPTER | opustags -i file.opus -S

When a tag value spans multiple lines, you would need to filter out the continuation lines starting with TAB (\t) too. Some awk would do the trick.

fmang commented 5 months ago

After more consideration, I have decided to introduce a -z flag to delimit tags by the NUL character instead of line feeds. This took inspiration from GNU tools like grep or sed that use -z too for the same purpose and the same goal: supporting embedded newlines. By leveraging the power of grep and sed, many more use cases will be supported.

To support multi-line tags, the previous command can now be written as:

opustags -z file.opus | grep -z -v ^CHAPTER | opustags -z -i file.opus -S

This new option is ready in the master branch, and I plan to release it this week unless you have any remarks. I have documented this use case in the man page.

vargobj commented 4 months ago

Sounds like a good proposal. By replacing the strncasecmp with fnmatch we could get a working solution. We must ensure however that is not a valid character in tag names or, if it is, that there exists a way to escape the to match it literally. Alternatively, we could add an optional value to --delete-all and write -D=CHAPTER*. That latter option might be less surprising because it is plural.

As zvezdochiot suggested, you can use --set-all to replace all the tags at once. In your case, assuming they all fit in one line :

opustags file.opus | grep -v ^CHAPTER | opustags -i file.opus -S

-S replaces tags. It seems to me that if the old file had more chapter tags than the new one (or whatever) that this would still fail. Since, in the general case, I have no idea what is in there, don't care, and just want them all removed to be safe.\

When a tag value spans multiple lines, you would need to filter out the continuation lines starting with TAB (\t) too. Some awk would do the trick.

I did not go to the trouble of individually removing the CHAPTER tags, i.e., while still preserving all of the remaining metadata, just, instead, to delete everything and start from scratch. No, I didn't miss those flags.

I suppose the grep step takes care of the fact that in order to use -d you need to know the tags in the first place? I.e., by grepping them, you've obviated the need to know each one?

So bottom line, are gobs accepted now ... only through grep though?

fmang commented 4 months ago

The idea of the change is you can relay complex edition to an external program. In your case, you can use grep to delete tags matching a pattern. Note that in my command line, the file.opus is the same on both ends, and that’s a key point.

The first opustags reads the tags from the file, so you’d get something like:

ARTIST=Someone
TITLE=Something
CHAPTER1=…
CHAPTER2=…

Then, grep -v removes the tags starting with CHAPTER, so you get:

ARTIST=Someone
TITLE=Something

Finally, the last opustags -S writes the filtered tags back to the original file.

Feel free to experiment with my last command (with all the -z) on a copy of the files you’d like to process and see the result.