Skip to content

Meta Editor

NzbMetaEditor

NzbMetaEditor(nzb: str)

Create an NzbMetaEditor instance.

Parameters:

Name Type Description Default
nzb str

NZB content as a string.

required

Raises:

Type Description
InvalidNzbError

If the string cannot be parsed as valid XML.

Note

This does not validate the given NZB.

Example
from nzb import NzbMetaEditor

text = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nzb PUBLIC "-//newzBin//DTD NZB 1.1//EN" "http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd">
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
    <head>
        <meta type="title">Big Buck Bunny - S01E01.mkv</meta>
        <meta type="password">secret</meta>
        <meta type="tag">HD</meta>
        <meta type="category">TV</meta>
    </head>
    <file poster="John &lt;nzb@nowhere.example&gt;" date="1706440708" subject="[1/1] - &quot;Big Buck Bunny - S01E01.mkv&quot; yEnc (1/2) 1478616">
        <groups>
            <group>alt.binaries.boneless</group>
        </groups>
        <segments>
            <segment bytes="739067" number="1">9cacde4c986547369becbf97003fb2c5-9483514693959@example</segment>
            <segment bytes="739549" number="2">70a3a038ce324e618e2751e063d6a036-7285710986748@example</segment>
        </segments>
    </file>
</nzb>
'''

editor = NzbMetaEditor(text)
edited = editor.set(title="Big Buck Bunny").append(tags="1080p").to_str()
print(edited)
Source code in src/nzb/_core.py
def __init__(self, nzb: str, /) -> None:
    """
    Create an NzbMetaEditor instance.

    Parameters
    ----------
    nzb : str
        NZB content as a string.

    Raises
    ------
    InvalidNzbError
        If the string cannot be parsed as valid XML.

    Note
    ----
    This does not validate the given NZB.

    Example
    -------
    ```python
    from nzb import NzbMetaEditor

    text = '''
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE nzb PUBLIC "-//newzBin//DTD NZB 1.1//EN" "http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd">
    <nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
        <head>
            <meta type="title">Big Buck Bunny - S01E01.mkv</meta>
            <meta type="password">secret</meta>
            <meta type="tag">HD</meta>
            <meta type="category">TV</meta>
        </head>
        <file poster="John &lt;nzb@nowhere.example&gt;" date="1706440708" subject="[1/1] - &quot;Big Buck Bunny - S01E01.mkv&quot; yEnc (1/2) 1478616">
            <groups>
                <group>alt.binaries.boneless</group>
            </groups>
            <segments>
                <segment bytes="739067" number="1">9cacde4c986547369becbf97003fb2c5-9483514693959@example</segment>
                <segment bytes="739549" number="2">70a3a038ce324e618e2751e063d6a036-7285710986748@example</segment>
            </segments>
        </file>
    </nzb>
    '''

    editor = NzbMetaEditor(text)
    edited = editor.set(title="Big Buck Bunny").append(tags="1080p").to_str()
    print(edited)
    ```

    """
    self._nzb = nzb
    self._nzbdict = nzb_to_dict(self._nzb)

append

append(
    *,
    passwords: Iterable[str] | str | None = None,
    tags: Iterable[str] | str | None = None,
) -> Self

Append metadata fields to the existing metadata in the NZB.

Parameters:

Name Type Description Default
passwords Iterable[str] | str

Password(s) for the NZB file.

None
tags Iterable[str] | str

Tag(s) associated with the NZB file.

None

Returns:

Type Description
Self

Returns itself.

Source code in src/nzb/_core.py
def append(
    self,
    *,
    passwords: Iterable[str] | str | None = None,
    tags: Iterable[str] | str | None = None,
) -> Self:
    """
    Append metadata fields to the existing metadata in the NZB.

    Parameters
    ----------
    passwords : Iterable[str] | str, optional
        Password(s) for the NZB file.
    tags : Iterable[str] | str, optional
        Tag(s) associated with the NZB file.

    Returns
    -------
    Self
        Returns itself.

    """

    meta = self._get_meta()

    if meta is None:
        return self.set(passwords=passwords, tags=tags)

    new_meta = [meta] if isinstance(meta, dict) else meta
    new_meta.extend(construct_meta(passwords=passwords, tags=tags))

    self._nzbdict["nzb"]["head"]["meta"] = sort_meta(new_meta)

    return self

clear

clear() -> Self

Clear all metadata fields from the NZB.

Returns:

Type Description
Self

Returns itself.

Source code in src/nzb/_core.py
def clear(self) -> Self:
    """
    Clear all metadata fields from the NZB.

    Returns
    -------
    Self
        Returns itself.

    """
    try:
        del self._nzbdict["nzb"]["head"]
    except KeyError:
        pass

    return self

from_file classmethod

from_file(nzb: StrPath) -> Self

Create an NzbMetaEditor instance from an NZB file path.

Parameters:

Name Type Description Default
nzb StrPath

File path to the NZB.

required

Returns:

Type Description
Self

Returns itself.

Source code in src/nzb/_core.py
@classmethod
def from_file(cls, nzb: StrPath, /) -> Self:
    """
    Create an NzbMetaEditor instance from an NZB file path.

    Parameters
    ----------
    nzb : StrPath
        File path to the NZB.

    Returns
    -------
    Self
        Returns itself.

    """
    return cls(read_nzb_file(nzb))

remove

remove(
    key: Literal["title", "password", "tag", "category"],
) -> Self
remove(key: str) -> Self
remove(
    key: Literal["title", "password", "tag", "category"]
    | str,
) -> Self

Remove a metadata field from the NZB. If the same field is present multiple times, this will remove them all.

Parameters:

Name Type Description Default
key Literal['title', 'password', 'tag', 'category'] | str

The metadata field to remove.

required

Returns:

Type Description
Self

Returns itself.

Source code in src/nzb/_core.py
def remove(self, key: Literal["title", "password", "tag", "category"] | str) -> Self:
    """
    Remove a metadata field from the NZB.
    If the same field is present multiple times, this will remove them all.

    Parameters
    ----------
    key : Literal["title", "password", "tag", "category"] | str, optional
        The metadata field to remove.

    Returns
    -------
    Self
        Returns itself.

    """

    meta = self._get_meta()

    if meta is None:
        return self

    elif isinstance(meta, dict):
        if key == meta["@type"]:
            return self.clear()
        else:
            return self
    else:
        new_meta = [row for row in meta if row["@type"] != key]
        self._nzbdict["nzb"]["head"]["meta"] = sort_meta(new_meta)
        return self

set

set(
    *,
    title: str | None = None,
    passwords: Iterable[str] | str | None = None,
    tags: Iterable[str] | str | None = None,
    category: str | None = None,
) -> Self

Set metadata fields in the NZB. Provided fields are replaced entirely if they already exist. Fields that aren't provided remain unchanged.

Parameters:

Name Type Description Default
title str

The title metadata field.

None
passwords Iterable[str] | str

Password(s) for the NZB file.

None
tags Iterable[str] | str

Tag(s) associated with the NZB file.

None
category str

Category of the NZB file.

None

Returns:

Type Description
Self

Returns itself.

Source code in src/nzb/_core.py
def set(
    self,
    *,
    title: str | None = None,
    passwords: Iterable[str] | str | None = None,
    tags: Iterable[str] | str | None = None,
    category: str | None = None,
) -> Self:
    """
    Set metadata fields in the NZB.
    Provided fields are replaced entirely if they already exist.
    Fields that aren't provided remain unchanged.

    Parameters
    ----------
    title : str, optional
        The title metadata field.
    passwords : Iterable[str] | str, optional
        Password(s) for the NZB file.
    tags : Iterable[str] | str, optional
        Tag(s) associated with the NZB file.
    category : str, optional
        Category of the NZB file.

    Returns
    -------
    Self
        Returns itself.

    """

    if title is None and passwords is None and tags is None and category is None:
        return self

    meta = self._get_meta()

    if meta is None:
        nzb = OrderedDict(self._nzbdict["nzb"])
        nzb["head"] = {}
        nzb["head"]["meta"] = sort_meta(
            construct_meta(title=title, passwords=passwords, tags=tags, category=category)
        )
        nzb.move_to_end("file")
        self._nzbdict["nzb"] = nzb
        return self

    new_meta = [meta] if isinstance(meta, dict) else meta

    fields_to_remove = {
        "title": title is not None,
        "category": category is not None,
        "password": passwords is not None,
        "tag": tags is not None,
    }

    filtered_meta = remove_meta_fields(new_meta, [k for k, v in fields_to_remove.items() if v])
    filtered_meta.extend(construct_meta(title=title, passwords=passwords, tags=tags, category=category))
    self._nzbdict["nzb"]["head"]["meta"] = sort_meta(filtered_meta)

    return self

to_file

to_file(
    filename: StrPath, *, overwrite: bool = False
) -> Path

Save the edited NZB to a file.

Parameters:

Name Type Description Default
filename StrPath

Destination path for saving the NZB. This will also create the path if it doesn't exist already.

required
overwrite bool

Whether to overwrite the file if it exists, defaults to False.

False

Returns:

Type Description
Path

The path to the saved file.

Raises:

Type Description
FileExistsError

If the file exists and overwrite is False.

Source code in src/nzb/_core.py
def to_file(self, filename: StrPath, *, overwrite: bool = False) -> Path:
    """
    Save the edited NZB to a file.

    Parameters
    ----------
    filename : StrPath, optional
        Destination path for saving the NZB.
        This will also create the path if it doesn't exist already.
    overwrite : bool, optional
        Whether to overwrite the file if it exists, defaults to `False`.

    Returns
    -------
    Path
        The path to the saved file.

    Raises
    ------
    FileExistsError
        If the file exists and overwrite is `False`.

    """

    outfile = realpath(filename)

    if outfile.is_file() and not overwrite:
        raise FileExistsError(outfile)

    outfile.parent.mkdir(parents=True, exist_ok=True)
    outfile.write_text(self.to_str(), encoding="utf-8")
    return outfile.resolve()

to_str

to_str() -> str

Return the edited NZB as a string.

Returns:

Type Description
str

Edited NZB.

Source code in src/nzb/_core.py
def to_str(self) -> str:
    """
    Return the edited NZB as a string.

    Returns
    -------
    str
        Edited NZB.

    """
    unparsed = xmltodict.unparse(self._nzbdict, encoding="utf-8", pretty=True, indent="    ")

    if doctype := parse_doctype(self._nzb):
        # see: https://github.com/martinblech/xmltodict/issues/351
        nzb = unparsed.splitlines()
        nzb.insert(1, doctype)
        return "\n".join(nzb)

    return unparsed.strip()