Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.2.0] - 2026-06-04

### Added

- Added class `Tag` and `TagType`.
- Added method `tags_detailed` to `Post` class.
- Added make recipe for setting up all dev dependencies

### Changed

- Updated documentation
- Updated mock response.yaml


## [4.1.0] - 2026-05-02

### Added
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ VERSION = $(shell $(PYTHON3) scripts/read_pyproject.py project/version)
# Binaries
POETRY ?= poetry $(POETRY_ARGS)
POETRY_ARGS ?=
PYTHON3 ?= $(POETRY) run python3
PYTHON3 ?= $(POETRY) run python3 -X utf8

PYTEST = $(POETRY) run pytest $(PYTEST_ARGS)
PYTEST_ARGS ?=
Expand Down Expand Up @@ -105,3 +105,9 @@ mostlyclean :
rm -rf $(builddir)
find ./ -depth -path '**/rule34Py.egg-info*' -print -delete
.PHONY : mostlyclean


# Setup all stuff
setup_dev:
pip install sphinx_mdinclude sphinx_rtd_theme
.PHONY: setup_dev
1 change: 1 addition & 0 deletions docs/api/rule34Py/__init__.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ rule34Py
rule34
toptag
autocomplete_tag
tag
5 changes: 5 additions & 0 deletions docs/api/rule34Py/tag.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rule34Py.tag
===============

.. automodule:: rule34Py.tag
:members:
13 changes: 5 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ maintainers = [
]
readme = "README.md"
requires-python = ">=3.9, <4.0"
version = "4.1.0"
version = "4.2.0"


[project.urls]
Expand Down
28 changes: 22 additions & 6 deletions rule34Py/post.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# rule34Py - Python api wrapper for rule34.xxx
#
# Copyright (C) 2022-2025 b3yc0d3 <b3yc0d3@gmail.com>
# Copyright (C) 2022-2026 b3yc0d3 <b3yc0d3@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -19,6 +19,11 @@

# TODO: Restructure internal variable names

from typing import List
import warnings

from rule34Py.tag import Tag


class Post:
"""A Rule34 Post object.
Expand Down Expand Up @@ -61,12 +66,17 @@ def from_json(json: str) -> "Post":
sample = json["sample_url"]
change = json["change"]
directory = json["directory"]

pTagsObj = []
if "tag_info" in json:
for tag in json["tag_info"]:
pTagsObj.append(Tag.from_json(tag))

img_type = "video" if pFileUrl.endswith(".mp4") else "gif" if pFileUrl.endswith(".gif") else "image"

return Post(pId, pHash, pScore, pSize, pFileUrl, preview, sample, pOwner, pTags, img_type, directory, change)
return Post(pId, pHash, pScore, pSize, pFileUrl, preview, sample, pOwner, pTags, pTagsObj, img_type, directory, change)

def __init__(self, id: int, hash: str, score: int, size: list, image: str, preview: str, sample: str, owner: str, tags: list, file_type: str, directory: int, change: int):
def __init__(self, id: int, hash: str, score: int, size: list, image: str, preview: str, sample: str, owner: str, tags_str: List[str], tags_obj: List[Tag], file_type: str, directory: int, change: int):
"""Create a new Post object."""
self._file_type = file_type
self._video = ""
Expand All @@ -84,10 +94,11 @@ def __init__(self, id: int, hash: str, score: int, size: list, image: str, previ
self._preview = preview
self._sample = sample
self._owner = owner
self._tags = tags
self._tags_str = tags_str # keep old code working
self._directory = directory
self._change = change
self._rating = None
self._tags_obj = tags_obj


@property
Expand Down Expand Up @@ -184,13 +195,18 @@ def owner(self) -> str:
return self._owner

@property
def tags(self) -> list:
def tags(self) -> List[Tag]:
"""The Post's tags.

Warning:
This property no longer returns a list of strings, instead it now returns
a list of :py:class:`Tag` objects.

Returns:
A List of the Post's tags.
"""
return self._tags

return self._tags_obj

@property
def content_type(self) -> str:
Expand Down
5 changes: 5 additions & 0 deletions rule34Py/rule34.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ def get_post(self, post_id: int) -> Union[Post, None]:
""" # noqa: DOC502
params = [["POST_ID", str(post_id)]]
formatted_url = self._parseUrlParams(API_URLS.GET_POST.value, params)
# Add "fields"
formatted_url += "&fields=tag_info"
response = self._get(formatted_url)
response.raise_for_status()

Expand Down Expand Up @@ -484,6 +486,9 @@ def search(
url += f"&pid={{PAGE_ID}}"
params.append(["PAGE_ID", str(page_id)])

# Add "fields"
url += "&fields=tag_info"

formatted_url = self._parseUrlParams(url, params)
response = self._get(formatted_url)
response.raise_for_status()
Expand Down
144 changes: 144 additions & 0 deletions rule34Py/tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# rule34Py - Python api wrapper for rule34.xxx
#
# Copyright (C) 2026 b3yc0d3 <b3yc0d3@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""A module containing the Tag class."""

from enum import Enum

class TagType(Enum):
"""
Possible type of a tag
"""

ARTIST = 1
"""
Artist name
"""
CHARACTER = 2
"""
Character name
"""
COPYRIGHT = 3
"""
Copyright holder
"""
METADATA = 4
"""
Metadata generic
"""
TAG = 0
"""
Tag generic
"""

@staticmethod
def from_str(data: str):
"""Convert str into TagType.

Args:
data (str): Lowercase representation of tag type.

Returns:
Type of Tag
"""

data = data.lower()

if data == "artist":
return TagType.ARTIST
elif data == "character":
return TagType.CHARACTER
elif data == "copyright":
return TagType.COPYRIGHT
else:
return TagType.TAG

# TODO: implement also STR methods for this class
class Tag:
"""A Rule34 Tag object.

Note:
This object can behave as a normal string, for backwards compatibility.

Parameters:
count: Usage count of the Tag.
type: Type of the Tag.
tag: String value of the Tag.
"""

count: int
type: TagType
tag: str

@staticmethod
def from_json(json: dict):
"""Create Tag class instance from JSON data.

Args:
json (dict): Tag json object.
"""

_count = int(json["count"])
_type = TagType.from_str(json["type"])
_tag = json["tag"]

return Tag(_count, _type, _tag)

def __init__(self, count: int, type: TagType, tag: str):
"""
Create a new Tag object.
"""

self._count = count
self._type = type
self._tag = tag

@property
def count(self) -> int:
"""Usage count of the Tag.

Returns:
The count how often the Tag is used.
"""
return self._count

@property
def type(self) -> TagType:
"""Type of the Tag

Returns:
The type of the current tag
"""
return self._type

@property
def tag(self) -> str:
"""Value of the Tag.

Returns:
The string value of the tag.
"""
return self._tag

def __str__(self) -> str:
return self.tag

def __getattr__(self, name: str):
"""
Is here for backwards compatibility
"""
return getattr(self.tag, name)
Loading
Loading