From 810f0d2b21218fedb95e15c8d7a281999e25ad45 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 10:38:46 +0800 Subject: [PATCH 01/10] Refactor error handling documentation to standardize exception class names and improve clarity --- docs/api/dftt_timecode.rst | 54 ------------------ docs/api/dftt_timerange.rst | 48 ++-------------- docs/api/error.rst | 106 ++++++++++++++++++++++++------------ 3 files changed, 77 insertions(+), 131 deletions(-) diff --git a/docs/api/dftt_timecode.rst b/docs/api/dftt_timecode.rst index db62914..680c6d1 100644 --- a/docs/api/dftt_timecode.rst +++ b/docs/api/dftt_timecode.rst @@ -14,60 +14,6 @@ Core Class The :class:`DfttTimecode` class is the main interface for working with timecodes in various formats. -Constructor -~~~~~~~~~~~ - -.. automethod:: DfttTimecode.__init__ - -Properties -~~~~~~~~~~ - -.. autoproperty:: DfttTimecode.type -.. autoproperty:: DfttTimecode.fps -.. autoproperty:: DfttTimecode.framecount -.. autoproperty:: DfttTimecode.timestamp -.. autoproperty:: DfttTimecode.is_drop_frame -.. autoproperty:: DfttTimecode.is_strict -.. autoproperty:: DfttTimecode.precise_timestamp - -Methods -~~~~~~~ - -.. automethod:: DfttTimecode.set_fps -.. automethod:: DfttTimecode.set_type -.. automethod:: DfttTimecode.set_strict -.. automethod:: DfttTimecode.timecode_output - -Operators -~~~~~~~~~ - -Arithmetic Operators -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: DfttTimecode.__add__ -.. automethod:: DfttTimecode.__sub__ -.. automethod:: DfttTimecode.__mul__ -.. automethod:: DfttTimecode.__truediv__ -.. automethod:: DfttTimecode.__neg__ - -Comparison Operators -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: DfttTimecode.__eq__ -.. automethod:: DfttTimecode.__ne__ -.. automethod:: DfttTimecode.__lt__ -.. automethod:: DfttTimecode.__le__ -.. automethod:: DfttTimecode.__gt__ -.. automethod:: DfttTimecode.__ge__ - -Type Conversion Operators -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: DfttTimecode.__int__ -.. automethod:: DfttTimecode.__float__ -.. automethod:: DfttTimecode.__str__ -.. automethod:: DfttTimecode.__repr__ - Supported Timecode Types ------------------------- diff --git a/docs/api/dftt_timerange.rst b/docs/api/dftt_timerange.rst index 37b4f01..7be49a6 100644 --- a/docs/api/dftt_timerange.rst +++ b/docs/api/dftt_timerange.rst @@ -14,34 +14,6 @@ Core Class The :class:`DfttTimeRange` class represents a time range with start and end points, built on top of :class:`DfttTimecode`. -Constructor -~~~~~~~~~~~ - -.. automethod:: DfttTimeRange.__init__ - -Properties -~~~~~~~~~~ - -.. autoproperty:: DfttTimeRange.start -.. autoproperty:: DfttTimeRange.end -.. autoproperty:: DfttTimeRange.duration - -Methods -~~~~~~~ - -.. automethod:: DfttTimeRange.contains -.. automethod:: DfttTimeRange.overlaps -.. automethod:: DfttTimeRange.intersect - -Operators -~~~~~~~~~ - -.. automethod:: DfttTimeRange.__contains__ -.. automethod:: DfttTimeRange.__eq__ -.. automethod:: DfttTimeRange.__ne__ -.. automethod:: DfttTimeRange.__str__ -.. automethod:: DfttTimeRange.__repr__ - Examples -------- @@ -60,8 +32,8 @@ Basic Usage # Get duration print(range1.duration.timecode_output('smpte')) # '01:00:00:00' -Checking Containment -~~~~~~~~~~~~~~~~~~~~ +Checking Containment and Intersection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python @@ -71,20 +43,12 @@ Checking Containment if tc in range1: print("Timecode is within range") -Checking Overlaps -~~~~~~~~~~~~~~~~~ - -.. code-block:: python - + # Create another range range2 = DfttTimeRange( DfttTimecode('01:30:00:00', 'auto', fps=24), DfttTimecode('02:30:00:00', 'auto', fps=24) ) - # Check if ranges overlap - if range1.overlaps(range2): - print("Ranges overlap") - - # Get intersection - intersection = range1.intersect(range2) - print(intersection) + # Get intersection of two ranges + intersection = range1.intersect(range2) + print(intersection) diff --git a/docs/api/error.rst b/docs/api/error.rst index 2ee737f..9aa1544 100644 --- a/docs/api/error.rst +++ b/docs/api/error.rst @@ -8,50 +8,86 @@ Exception Classes The dftt_timecode library defines custom exception classes for different error conditions. -DfttTimecodeError -~~~~~~~~~~~~~~~~~ +DFTTError +~~~~~~~~~ -.. autoclass:: DfttTimecodeError +.. autoclass:: DFTTError :members: :show-inheritance: Base exception class for all dftt_timecode errors. -DfttTimecodeValueError +DFTTTimecodeValueError ~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: DfttTimecodeValueError +.. autoclass:: DFTTTimecodeValueError :members: :show-inheritance: Raised when an invalid timecode value is provided. -DfttTimecodeTypeError +DFTTTimecodeInitializationError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DFTTTimecodeInitializationError + :members: + :show-inheritance: + +Raised when timecode initialization fails due to incompatible parameters. + +DFTTTimecodeTypeError ~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: DfttTimecodeTypeError +.. autoclass:: DFTTTimecodeTypeError :members: :show-inheritance: Raised when an invalid timecode type is specified or type mismatch occurs. -DfttTimecodeFPSError -~~~~~~~~~~~~~~~~~~~~ +DFTTTimecodeOperatorError +~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: DfttTimecodeFPSError +.. autoclass:: DFTTTimecodeOperatorError :members: :show-inheritance: -Raised when an invalid frame rate is provided. +Raised when an arithmetic or comparison operation on timecode objects fails. -DfttTimecodeDropFrameError -~~~~~~~~~~~~~~~~~~~~~~~~~~ +DFTTTimeRangeMethodError +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DFTTTimeRangeMethodError + :members: + :show-inheritance: + +Raised when a timerange method is called with invalid parameters or conditions. + +DFTTTimeRangeValueError +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DFTTTimeRangeValueError + :members: + :show-inheritance: -.. autoclass:: DfttTimecodeDropFrameError +Raised when a timerange value is invalid or out of acceptable range. + +DFTTTimeRangeTypeError +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DFTTTimeRangeTypeError :members: :show-inheritance: -Raised when there's an issue with drop-frame timecode handling. +Raised when a timerange type or operand type is invalid. + +DFTTTimeRangeFPSError +~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: DFTTTimeRangeFPSError + :members: + :show-inheritance: + +Raised when timerange operations fail due to frame rate mismatches. Error Examples -------------- @@ -62,52 +98,52 @@ Invalid Timecode Value .. code-block:: python from dftt_timecode import DfttTimecode - from dftt_timecode.error import DfttTimecodeValueError + from dftt_timecode.error import DFTTTimecodeValueError try: tc = DfttTimecode('99:99:99:99', 'smpte', fps=24) - except DfttTimecodeValueError as e: + except DFTTTimecodeValueError as e: print(f"Invalid timecode: {e}") -Invalid Frame Rate -~~~~~~~~~~~~~~~~~~ +Timecode Initialization Error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from dftt_timecode import DfttTimecode - from dftt_timecode.error import DfttTimecodeFPSError + from dftt_timecode.error import DFTTTimecodeInitializationError try: - tc = DfttTimecode('01:00:00:00', 'auto', fps=-24) - except DfttTimecodeFPSError as e: - print(f"Invalid FPS: {e}") + # Drop-frame status mismatch with timecode format + tc = DfttTimecode('01:00:00:00', 'smpte', fps=29.97, drop_frame=False) + except DFTTTimecodeInitializationError as e: + print(f"Initialization error: {e}") -Drop-Frame Error -~~~~~~~~~~~~~~~~ +Type Error +~~~~~~~~~~ .. code-block:: python from dftt_timecode import DfttTimecode - from dftt_timecode.error import DfttTimecodeDropFrameError + from dftt_timecode.error import DFTTTimecodeTypeError try: - # Invalid drop-frame timecode (frames 00 and 01 should be dropped at minute boundaries) - tc = DfttTimecode('00:01:00:00', 'smpte', fps=29.97, drop_frame=True) - except DfttTimecodeDropFrameError as e: - print(f"Drop-frame error: {e}") + tc = DfttTimecode('invalid_format', 'unknown_type', fps=24) + except DFTTTimecodeTypeError as e: + print(f"Type error: {e}") -Type Mismatch -~~~~~~~~~~~~~ +Operator Error +~~~~~~~~~~~~~~ .. code-block:: python from dftt_timecode import DfttTimecode - from dftt_timecode.error import DfttTimecodeTypeError + from dftt_timecode.error import DFTTTimecodeOperatorError try: tc1 = DfttTimecode('01:00:00:00', 'auto', fps=24) tc2 = DfttTimecode('01:00:00:00', 'auto', fps=30) # Cannot add timecodes with different frame rates result = tc1 + tc2 - except DfttTimecodeTypeError as e: - print(f"Type error: {e}") + except DFTTTimecodeOperatorError as e: + print(f"Operator error: {e}") From d496f62ed4a5930234a34b0b51449330973a294b Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 11:04:28 +0800 Subject: [PATCH 02/10] Enhance documentation workflow and structure by updating module paths, improving navigation settings, and refining version handling for better clarity and usability. --- .github/workflows/docs.yml | 85 +++++++++++++++++++++++++++++++++++-- docs/api/dftt_timecode.rst | 2 +- docs/api/dftt_timerange.rst | 2 +- docs/conf.py | 26 +++++++++--- docs/index.rst | 3 -- 5 files changed, 103 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c0d462f..a727514 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,6 +5,8 @@ on: branches: - main - dev + tags: + - '*' pull_request: branches: - main @@ -26,6 +28,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all tags and branches - name: Set up Python uses: actions/setup-python@v5 @@ -39,19 +43,94 @@ jobs: pip install sphinx pydata-sphinx-theme pip install -e . + - name: Determine version + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + # Tag push - use tag name as version + VERSION=${GITHUB_REF#refs/tags/} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "is_tag=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then + # Main branch - use "stable" or latest tag + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev") + echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "is_tag=false" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then + # Dev branch - use "dev" + echo "version=dev" >> $GITHUB_OUTPUT + echo "is_tag=false" >> $GITHUB_OUTPUT + else + # Other branches + echo "version=preview" >> $GITHUB_OUTPUT + echo "is_tag=false" >> $GITHUB_OUTPUT + fi + - name: Build documentation run: | cd docs sphinx-build -b html . _build/html + - name: Prepare multi-version structure + run: | + VERSION="${{ steps.version.outputs.version }}" + mkdir -p docs/_build/pages/$VERSION + cp -r docs/_build/html/* docs/_build/pages/$VERSION/ + + # If this is main branch, also copy to root and create stable link + if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then + cp -r docs/_build/html/* docs/_build/pages/ + mkdir -p docs/_build/pages/stable + cp -r docs/_build/html/* docs/_build/pages/stable/ + fi + + - name: Update version switcher + run: | + VERSION="${{ steps.version.outputs.version }}" + mkdir -p docs/_build/pages/_static + + # Create or update switcher.json + cat > docs/_build/pages/_static/switcher.json << 'EOF' + [ + { + "name": "dev (latest)", + "version": "dev", + "url": "https://owenyou.github.io/dftt_timecode/dev/" + }, + { + "name": "stable", + "version": "stable", + "url": "https://owenyou.github.io/dftt_timecode/stable/" + }, + { + "name": "0.0.15a2", + "version": "0.0.15a2", + "url": "https://owenyou.github.io/dftt_timecode/0.0.15a2/" + }, + { + "name": "0.0.15a1", + "version": "0.0.15a1", + "url": "https://owenyou.github.io/dftt_timecode/0.0.15a1/" + }, + { + "name": "0.0.14", + "version": "0.0.14", + "url": "https://owenyou.github.io/dftt_timecode/0.0.14/" + } + ] + EOF + + # Copy switcher.json to each version directory + find docs/_build/pages -type d -name "_static" -exec cp docs/_build/pages/_static/switcher.json {} \; + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: docs/_build/html + path: docs/_build/pages deploy: - # Only deploy on pushes to main branch - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + # Only deploy on pushes to main, dev, or tags + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) environment: name: github-pages diff --git a/docs/api/dftt_timecode.rst b/docs/api/dftt_timecode.rst index 680c6d1..44a5d57 100644 --- a/docs/api/dftt_timecode.rst +++ b/docs/api/dftt_timecode.rst @@ -1,7 +1,7 @@ DfttTimecode API ================ -.. currentmodule:: dftt_timecode +.. currentmodule:: dftt_timecode.core.dftt_timecode .. autoclass:: DfttTimecode :members: diff --git a/docs/api/dftt_timerange.rst b/docs/api/dftt_timerange.rst index 7be49a6..844576c 100644 --- a/docs/api/dftt_timerange.rst +++ b/docs/api/dftt_timerange.rst @@ -1,7 +1,7 @@ DfttTimeRange API ================= -.. currentmodule:: dftt_timecode +.. currentmodule:: dftt_timecode.core.dftt_timerange .. autoclass:: DfttTimeRange :members: diff --git a/docs/conf.py b/docs/conf.py index c260f96..d1372a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,20 +53,23 @@ "show_toc_level": 2, "navbar_align": "left", "navigation_with_keys": False, - "show_nav_level": 1, # Show from top level + "show_nav_level": 2, # Show navigation levels in sidebar "navigation_depth": 4, - "collapse_navigation": True, - "sidebar_includehidden": True, # Include hidden toctree items in sidebar + "collapse_navigation": False, # Keep navigation expanded "logo": { "text": "DFTT Timecode", }, - "navbar_end": ["navbar-icon-links"], - "primary_sidebar_end": ["sidebar-ethical-ads"], + "navbar_end": ["version-switcher", "navbar-icon-links"], + "switcher": { + "json_url": "https://owenyou.github.io/dftt_timecode/_static/switcher.json", + "version_match": release, + }, } # Enable the global table of contents in sidebar +# Removed "search-button" from sidebar to hide search html_sidebars = { - "**": ["search-field", "sidebar-nav-bs"] + "**": ["sidebar-nav-bs"] } html_context = { @@ -85,6 +88,14 @@ 'exclude-members': '__weakref__' } +# Avoid documenting imports as duplicates +autodoc_typehints = 'description' +autodoc_class_signature = 'separated' + +# Control which module path to use for imported members +# This prevents duplicate documentation when a class is imported into __init__.py +add_module_names = False + # Napoleon settings for Google/NumPy style docstrings napoleon_google_docstring = True napoleon_numpy_docstring = True @@ -107,4 +118,5 @@ } # -- Options for autosummary ------------------------------------------------ -autosummary_generate = True +# Disable autosummary generation to avoid duplicate documentation +autosummary_generate = False diff --git a/docs/index.rst b/docs/index.rst index 0a5c399..46d575a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,7 +70,6 @@ Contents .. toctree:: :maxdepth: 2 :caption: User Guide - :titlesonly: installation quickstart @@ -79,7 +78,6 @@ Contents .. toctree:: :maxdepth: 2 :caption: API Reference - :titlesonly: api/dftt_timecode api/dftt_timerange @@ -88,7 +86,6 @@ Contents .. toctree:: :maxdepth: 1 :caption: Development - :titlesonly: contributing changelog From 9a32c038e036346910e22f82c3303a16102c37c1 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 11:08:21 +0800 Subject: [PATCH 03/10] update github action --- .github/workflows/docs.yml | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a727514..647cff3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -66,31 +66,13 @@ jobs: echo "is_tag=false" >> $GITHUB_OUTPUT fi - - name: Build documentation - run: | - cd docs - sphinx-build -b html . _build/html - - - name: Prepare multi-version structure - run: | - VERSION="${{ steps.version.outputs.version }}" - mkdir -p docs/_build/pages/$VERSION - cp -r docs/_build/html/* docs/_build/pages/$VERSION/ - - # If this is main branch, also copy to root and create stable link - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - cp -r docs/_build/html/* docs/_build/pages/ - mkdir -p docs/_build/pages/stable - cp -r docs/_build/html/* docs/_build/pages/stable/ - fi - - - name: Update version switcher + - name: Prepare switcher.json before build run: | - VERSION="${{ steps.version.outputs.version }}" - mkdir -p docs/_build/pages/_static + # Ensure _static directory exists + mkdir -p docs/_static # Create or update switcher.json - cat > docs/_build/pages/_static/switcher.json << 'EOF' + cat > docs/_static/switcher.json << 'EOF' [ { "name": "dev (latest)", @@ -120,8 +102,30 @@ jobs: ] EOF - # Copy switcher.json to each version directory - find docs/_build/pages -type d -name "_static" -exec cp docs/_build/pages/_static/switcher.json {} \; + - name: Build documentation + run: | + cd docs + sphinx-build -b html . _build/html + + - name: Prepare multi-version structure + run: | + VERSION="${{ steps.version.outputs.version }}" + mkdir -p docs/_build/pages/$VERSION + cp -r docs/_build/html/* docs/_build/pages/$VERSION/ + + # If this is main branch, also copy to root and create stable link + if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then + cp -r docs/_build/html/* docs/_build/pages/ + mkdir -p docs/_build/pages/stable + cp -r docs/_build/html/* docs/_build/pages/stable/ + fi + + - name: Ensure switcher.json in all version directories + run: | + # Copy switcher.json to the root _static if it exists + if [ -f docs/_static/switcher.json ]; then + find docs/_build/pages -type d -name "_static" -exec cp docs/_static/switcher.json {} \; + fi - name: Upload artifact uses: actions/upload-pages-artifact@v3 From edbac37f8681345001e93ee9a6e482fa5a85e3ad Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 11:16:06 +0800 Subject: [PATCH 04/10] update github action --- .github/workflows/docs.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 647cff3..d6a2823 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,7 +23,7 @@ concurrency: jobs: build: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - name: Checkout repository @@ -40,7 +40,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install sphinx pydata-sphinx-theme + pip install -r requirements.txt pip install -e . - name: Determine version @@ -53,8 +53,8 @@ jobs: echo "is_tag=true" >> $GITHUB_OUTPUT elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then # Main branch - use "stable" or latest tag - LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev") - echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "stable") + echo "version=stable" >> $GITHUB_OUTPUT echo "is_tag=false" >> $GITHUB_OUTPUT elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then # Dev branch - use "dev" @@ -102,10 +102,22 @@ jobs: ] EOF - - name: Build documentation + - name: Build documentation with make run: | cd docs - sphinx-build -b html . _build/html + make html + + - name: Debug - Check build output + run: | + echo "=== Checking _build/html structure ===" + ls -la docs/_build/html/ + echo "=== Checking for _static ===" + ls -la docs/_build/html/_static/ || echo "No _static directory" + echo "=== Checking index.html ===" + if [ -f docs/_build/html/index.html ]; then + echo "index.html exists" + head -50 docs/_build/html/index.html + fi - name: Prepare multi-version structure run: | From 74dafc19737ab97f455deb7579b1bbf6e87d18e9 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 13:22:01 +0800 Subject: [PATCH 05/10] setup uv --- .github/workflows/docs.yml | 13 +- .github/workflows/publish-to-pypi.yml | 10 +- pyproject.toml | 59 +++ requirements.txt | 7 - setup.py | 30 +- uv.lock | 506 ++++++++++++++++++++++++++ 6 files changed, 584 insertions(+), 41 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt create mode 100644 uv.lock diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d6a2823..5be967e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,13 +35,14 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.11' - cache: 'pip' + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -e . + run: uv sync - name: Determine version id: version @@ -105,7 +106,7 @@ jobs: - name: Build documentation with make run: | cd docs - make html + uv run make html - name: Debug - Check build output run: | diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 5a477d1..a936edc 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -11,17 +11,15 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Build package - run: python -m build + run: uv build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c89b76b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,59 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "dftt-timecode" +version = "0.0.15a2" +description = "Timecode library for film and TV industry, supports HFR and a bunch of cool features" +readme = "README.md" +requires-python = ">=3.11" +license = "LGPL-2.1" +authors = [ + {name = "You Ziyuan", email = "hikaridragon0216@gmail.com"} +] +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Development Status :: 2 - Pre-Alpha", + "Natural Language :: Chinese (Simplified)", + "Operating System :: OS Independent", +] +keywords = ["timecode", "video", "film", "television", "smpte", "hfr"] + +dependencies = [] + +[project.optional-dependencies] +dev = [ + "pytest>=8.3.4", +] +docs = [ + "sphinx>=7.0.0", + "pydata-sphinx-theme>=0.15.0", +] +all = [ + "pytest>=8.3.4", + "sphinx>=7.0.0", + "pydata-sphinx-theme>=0.15.0", +] + +[project.urls] +Homepage = "https://github.com/OwenYou/dftt_timecode" +Documentation = "https://owenyou.github.io/dftt_timecode/" +Repository = "https://github.com/OwenYou/dftt_timecode" +Issues = "https://github.com/OwenYou/dftt_timecode/issues" + +[tool.setuptools.packages.find] +include = ["dftt_timecode*"] + +[tool.pytest.ini_options] +addopts = "-v -s" +testpaths = ["test"] + +[dependency-groups] +dev = [ + "pytest>=8.3.4", + "sphinx>=7.0.0", + "pydata-sphinx-theme>=0.15.0", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b23b6f6..0000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -iniconfig==2.0.0 -packaging==24.2 -pluggy==1.5.0 -pytest==8.3.4 -setuptools==75.8.0 -sphinx>=7.0.0 -pydata-sphinx-theme>=0.15.0 diff --git a/setup.py b/setup.py index 6c4cc4b..5bcb685 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,10 @@ +""" +Setup script for dftt_timecode. + +This file is maintained for backward compatibility. +The main configuration is in pyproject.toml. +""" import setuptools -import dftt_timecode as package -with open("README.md", "r",encoding='UTF-8') as fh: - long_description = fh.read() -setuptools.setup( - name=package.name, - version=package.__version__, - author="You Ziyuan", - author_email="hikaridragon0216@gmail.com", - description="Timecode library for film and TV industry, supports HFR and a bunch of cool features", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/OwenYou/dftt_timecode", - packages=['dftt_timecode','dftt_timecode.core'], - python_requires=">=3.11", - classifiers=[ - "Programming Language :: Python :: 3", - "Development Status :: 2 - Pre-Alpha", - "License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)", - "Natural Language :: Chinese (Simplified)", - "Operating System :: OS Independent", - ], -) +if __name__ == "__main__": + setuptools.setup() diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..2993532 --- /dev/null +++ b/uv.lock @@ -0,0 +1,506 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "dftt-timecode" +version = "0.0.15a2" +source = { editable = "." } + +[package.optional-dependencies] +all = [ + { name = "pydata-sphinx-theme" }, + { name = "pytest" }, + { name = "sphinx" }, +] +dev = [ + { name = "pytest" }, +] +docs = [ + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pydata-sphinx-theme" }, + { name = "pytest" }, + { name = "sphinx" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydata-sphinx-theme", marker = "extra == 'all'", specifier = ">=0.15.0" }, + { name = "pydata-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.15.0" }, + { name = "pytest", marker = "extra == 'all'", specifier = ">=8.3.4" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, + { name = "sphinx", marker = "extra == 'all'", specifier = ">=7.0.0" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = ">=7.0.0" }, +] +provides-extras = ["dev", "docs", "all"] + +[package.metadata.requires-dev] +dev = [ + { name = "pydata-sphinx-theme", specifier = ">=0.15.0" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "sphinx", specifier = ">=7.0.0" }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "babel" }, + { name = "beautifulsoup4" }, + { name = "docutils" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264, upload-time = "2024-12-17T10:53:35.645Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "roman-numerals-py" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, +] + +[[package]] +name = "sphinx" +version = "8.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals-py" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] From 10aff7acc6474ef79d8fb7d67cac049678258476 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 13:47:43 +0800 Subject: [PATCH 06/10] Enhance project metadata handling and documentation installation instructions; update version retrieval and author information. --- dftt_timecode/__init__.py | 14 +++++++-- docs/conf.py | 17 +++++++++-- docs/installation.rst | 63 ++++++++++++++++++++++++++++++++------- pyproject.toml | 17 ++--------- pytest.ini | 3 -- uv.lock | 23 -------------- 6 files changed, 81 insertions(+), 56 deletions(-) delete mode 100644 pytest.ini diff --git a/dftt_timecode/__init__.py b/dftt_timecode/__init__.py index a9a2339..86a614d 100644 --- a/dftt_timecode/__init__.py +++ b/dftt_timecode/__init__.py @@ -28,6 +28,18 @@ from dftt_timecode.core.dftt_timecode import DfttTimecode from dftt_timecode.core.dftt_timerange import DfttTimeRange +# Read version from package metadata (populated from pyproject.toml) +try: + from importlib.metadata import version, PackageNotFoundError + try: + __version__ = version("dftt-timecode") + except PackageNotFoundError: + # Package is not installed, use a fallback version + __version__ = "0.0.0+dev" +except ImportError: + # Python < 3.8 fallback (though we require 3.11+) + __version__ = "0.0.0+dev" + # Aliases for easier importing def timecode(*args, **kwargs) -> DfttTimecode: @@ -101,9 +113,7 @@ def dtr(*args, **kwargs) -> DfttTimeRange: """ return DfttTimeRange(*args, **kwargs) - name = "dftt_timecode" __author__ = "You Ziyuan" -__version__ = "0.0.15a2" __all__ = ["DfttTimecode", "DfttTimeRange", "timecode", "timerange", "dtc", "dtr"] diff --git a/docs/conf.py b/docs/conf.py index d1372a7..d8bf003 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,19 +5,32 @@ import os import sys +import tomllib # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. sys.path.insert(0, os.path.abspath('..')) +# -- Read project metadata from pyproject.toml ------------------------------- +# Get the path to pyproject.toml (one level up from docs/) +docs_dir = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.dirname(docs_dir) +pyproject_path = os.path.join(project_root, 'pyproject.toml') + +with open(pyproject_path, 'rb') as f: + pyproject_data = tomllib.load(f) + +project_info = pyproject_data['project'] + # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'DFTT Timecode' copyright = '2025, You Ziyuan' -author = 'You Ziyuan' -release = '0.0.15a1' +author = ', '.join(a['name'] for a in project_info['authors']) +release = project_info['version'] +version = release # Short version # HTML title html_title = 'DFTT Timecode' diff --git a/docs/installation.rst b/docs/installation.rst index 32bf33b..51b299e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -5,7 +5,7 @@ Requirements ------------ - Python >= 3.11 -- Standard library dependencies: +- Standard library dependencies only (no external dependencies required) - fractions - logging @@ -16,16 +16,40 @@ Requirements Installation from PyPI ---------------------- -The easiest way to install dftt_timecode is using pip: +The easiest way to install dftt_timecode is using pip or uv: .. code-block:: bash + # Using pip pip install dftt_timecode + # Using uv (recommended) + uv pip install dftt_timecode + Installation from Source ------------------------- -To install from source, clone the repository and install in development mode: +For development, we recommend using **uv** for faster and more reliable dependency management: + +.. code-block:: bash + + # Clone the repository + git clone https://github.com/OwenYou/dftt_timecode.git + cd dftt_timecode + + # Install uv if you haven't already + curl -LsSf https://astral.sh/uv/install.sh | sh + + # Sync dependencies and install in development mode + uv sync + +This will: + +- Create a virtual environment in ``.venv`` +- Install all development dependencies (pytest, sphinx, etc.) +- Install the package in editable mode + +Alternatively, using pip: .. code-block:: bash @@ -43,17 +67,34 @@ You can verify the installation by importing the package: import dftt_timecode print(dftt_timecode.__version__) -Dependencies ------------- +Development Dependencies +------------------------ + +The project uses **uv** for dependency management. All dependencies are defined in ``pyproject.toml``: + +- **pytest** - Testing framework +- **sphinx** - Documentation generator +- **pydata-sphinx-theme** - Documentation theme + +To install development dependencies with uv: + +.. code-block:: bash + + # Install all development dependencies + uv sync + + # Activate the virtual environment + source .venv/bin/activate # On Windows: .venv\Scripts\activate -For development, you may want to install additional dependencies: +To run tests: .. code-block:: bash - pip install -r requirements.txt + pytest -This includes: +To build documentation: + +.. code-block:: bash -- pytest for testing -- sphinx for documentation -- pydata-sphinx-theme for documentation theme + cd docs + make html diff --git a/pyproject.toml b/pyproject.toml index c89b76b..230911e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,8 @@ readme = "README.md" requires-python = ">=3.11" license = "LGPL-2.1" authors = [ - {name = "You Ziyuan", email = "hikaridragon0216@gmail.com"} + {name = "You Ziyuan", email = "hikaridragon0216@gmail.com"}, + {name = "Wheheo Hu", email = "wheheohu@outlook.com"} ] classifiers = [ "Programming Language :: Python :: 3", @@ -24,20 +25,6 @@ keywords = ["timecode", "video", "film", "television", "smpte", "hfr"] dependencies = [] -[project.optional-dependencies] -dev = [ - "pytest>=8.3.4", -] -docs = [ - "sphinx>=7.0.0", - "pydata-sphinx-theme>=0.15.0", -] -all = [ - "pytest>=8.3.4", - "sphinx>=7.0.0", - "pydata-sphinx-theme>=0.15.0", -] - [project.urls] Homepage = "https://github.com/OwenYou/dftt_timecode" Documentation = "https://owenyou.github.io/dftt_timecode/" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 1d1001a..0000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -addopts = -v -s -testpaths = test \ No newline at end of file diff --git a/uv.lock b/uv.lock index 2993532..469cf3a 100644 --- a/uv.lock +++ b/uv.lock @@ -141,20 +141,6 @@ name = "dftt-timecode" version = "0.0.15a2" source = { editable = "." } -[package.optional-dependencies] -all = [ - { name = "pydata-sphinx-theme" }, - { name = "pytest" }, - { name = "sphinx" }, -] -dev = [ - { name = "pytest" }, -] -docs = [ - { name = "pydata-sphinx-theme" }, - { name = "sphinx" }, -] - [package.dev-dependencies] dev = [ { name = "pydata-sphinx-theme" }, @@ -163,15 +149,6 @@ dev = [ ] [package.metadata] -requires-dist = [ - { name = "pydata-sphinx-theme", marker = "extra == 'all'", specifier = ">=0.15.0" }, - { name = "pydata-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.15.0" }, - { name = "pytest", marker = "extra == 'all'", specifier = ">=8.3.4" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, - { name = "sphinx", marker = "extra == 'all'", specifier = ">=7.0.0" }, - { name = "sphinx", marker = "extra == 'docs'", specifier = ">=7.0.0" }, -] -provides-extras = ["dev", "docs", "all"] [package.metadata.requires-dev] dev = [ From 04850a153b8b4e1950a464b703d851a69eb39b10 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 13:56:57 +0800 Subject: [PATCH 07/10] Refine HTML sidebar configuration by removing primary sidebar content and disabling left navigation for improved user experience. --- docs/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index d8bf003..c96c3cc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -77,12 +77,13 @@ "json_url": "https://owenyou.github.io/dftt_timecode/_static/switcher.json", "version_match": release, }, + "primary_sidebar_end": [], # Remove primary sidebar content + "show_nav_level": 0, # Hide navigation levels } -# Enable the global table of contents in sidebar -# Removed "search-button" from sidebar to hide search +# Disable the left sidebar navigation html_sidebars = { - "**": ["sidebar-nav-bs"] + "**": [] } html_context = { From 34927a114b4b7503578732f0d918cc8c9e8cd0d6 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 14:04:25 +0800 Subject: [PATCH 08/10] Refine deployment conditions to only allow pushes to main or tags, excluding dev branch for deployment. --- .github/workflows/docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5be967e..e5418f2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -146,8 +146,8 @@ jobs: path: docs/_build/pages deploy: - # Only deploy on pushes to main, dev, or tags - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) + # Only deploy on pushes to main or tags (dev builds but doesn't deploy) + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) environment: name: github-pages From 7a05d7a2a06440d32a85aee8a0c72bed17a36f24 Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 14:17:53 +0800 Subject: [PATCH 09/10] remove version feature for docs --- .github/workflows/docs.yml | 103 ++----------------------------------- docs/conf.py | 6 +-- 2 files changed, 5 insertions(+), 104 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e5418f2..7de4fc8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,9 +4,6 @@ on: push: branches: - main - - dev - tags: - - '*' pull_request: branches: - main @@ -28,8 +25,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - with: - fetch-depth: 0 # Fetch all history for all tags and branches - name: Set up Python uses: actions/setup-python@v5 @@ -44,110 +39,19 @@ jobs: - name: Install dependencies run: uv sync - - name: Determine version - id: version - run: | - if [[ "${{ github.ref }}" == refs/tags/* ]]; then - # Tag push - use tag name as version - VERSION=${GITHUB_REF#refs/tags/} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "is_tag=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - # Main branch - use "stable" or latest tag - LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "stable") - echo "version=stable" >> $GITHUB_OUTPUT - echo "is_tag=false" >> $GITHUB_OUTPUT - elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then - # Dev branch - use "dev" - echo "version=dev" >> $GITHUB_OUTPUT - echo "is_tag=false" >> $GITHUB_OUTPUT - else - # Other branches - echo "version=preview" >> $GITHUB_OUTPUT - echo "is_tag=false" >> $GITHUB_OUTPUT - fi - - - name: Prepare switcher.json before build - run: | - # Ensure _static directory exists - mkdir -p docs/_static - - # Create or update switcher.json - cat > docs/_static/switcher.json << 'EOF' - [ - { - "name": "dev (latest)", - "version": "dev", - "url": "https://owenyou.github.io/dftt_timecode/dev/" - }, - { - "name": "stable", - "version": "stable", - "url": "https://owenyou.github.io/dftt_timecode/stable/" - }, - { - "name": "0.0.15a2", - "version": "0.0.15a2", - "url": "https://owenyou.github.io/dftt_timecode/0.0.15a2/" - }, - { - "name": "0.0.15a1", - "version": "0.0.15a1", - "url": "https://owenyou.github.io/dftt_timecode/0.0.15a1/" - }, - { - "name": "0.0.14", - "version": "0.0.14", - "url": "https://owenyou.github.io/dftt_timecode/0.0.14/" - } - ] - EOF - - name: Build documentation with make run: | cd docs uv run make html - - name: Debug - Check build output - run: | - echo "=== Checking _build/html structure ===" - ls -la docs/_build/html/ - echo "=== Checking for _static ===" - ls -la docs/_build/html/_static/ || echo "No _static directory" - echo "=== Checking index.html ===" - if [ -f docs/_build/html/index.html ]; then - echo "index.html exists" - head -50 docs/_build/html/index.html - fi - - - name: Prepare multi-version structure - run: | - VERSION="${{ steps.version.outputs.version }}" - mkdir -p docs/_build/pages/$VERSION - cp -r docs/_build/html/* docs/_build/pages/$VERSION/ - - # If this is main branch, also copy to root and create stable link - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - cp -r docs/_build/html/* docs/_build/pages/ - mkdir -p docs/_build/pages/stable - cp -r docs/_build/html/* docs/_build/pages/stable/ - fi - - - name: Ensure switcher.json in all version directories - run: | - # Copy switcher.json to the root _static if it exists - if [ -f docs/_static/switcher.json ]; then - find docs/_build/pages -type d -name "_static" -exec cp docs/_static/switcher.json {} \; - fi - - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: docs/_build/pages + path: docs/_build/html deploy: - # Only deploy on pushes to main or tags (dev builds but doesn't deploy) - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + # Only deploy on pushes to main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' environment: name: github-pages @@ -160,3 +64,4 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + diff --git a/docs/conf.py b/docs/conf.py index c96c3cc..503052f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -72,11 +72,7 @@ "logo": { "text": "DFTT Timecode", }, - "navbar_end": ["version-switcher", "navbar-icon-links"], - "switcher": { - "json_url": "https://owenyou.github.io/dftt_timecode/_static/switcher.json", - "version_match": release, - }, + "navbar_end": ["navbar-icon-links"], "primary_sidebar_end": [], # Remove primary sidebar content "show_nav_level": 0, # Hide navigation levels } From 4bf68110d33b915fa807277c02f7320677f818fa Mon Sep 17 00:00:00 2001 From: WheheoHu Date: Mon, 20 Oct 2025 14:23:25 +0800 Subject: [PATCH 10/10] Update README --- README.md | 619 +++++++++++------------------------------------------- 1 file changed, 122 insertions(+), 497 deletions(-) diff --git a/README.md b/README.md index 57ea392..2f6ddfb 100644 --- a/README.md +++ b/README.md @@ -1,594 +1,219 @@ -# dftt_timecode +# DFTT Timecode [![pypi](https://img.shields.io/badge/pypi-0.0.14-brightgreen)](https://pypi.org/project/dftt-timecode/) -[![python](https://img.shields.io/badge/python-3-blue)]() +[![python](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/) [![GitHub license](https://img.shields.io/badge/license-LGPL2.1-green)](https://github.com/OwenYou/dftt_timecode/blob/main/LICENSE) +[![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://owenyou.github.io/dftt_timecode/) +A high-precision Python timecode library designed for film and TV production. -## 1. 简介 Introduction +为影视行业设计的高精度Python时码库。 -为影视行业设计的Python时码库,支持HFR高帧率以及其他丰富的功能。 +## Introduction | 简介 -Python timecode library for film and TV industry supports HFR and a bunch of cool features. +DFTT Timecode is a professional-grade timecode library for film and television post-production workflows. It provides frame-accurate calculations with support for multiple industry-standard formats, high frame rates, and drop-frame compensation. -DFTT是Department of Film and TV Technology of Beijing Film Academy的简称。 +DFTT Timecode是一个专业级的时码库,专为影视后期制作工作流设计。它提供帧精确的计算,支持多种行业标准格式、高帧率和丢帧补偿。 -DFTT stands for the short of Department of Film and Tv Technology of Beijing Film Academy. +**DFTT** stands for Department of Film and TV Technology of Beijing Film Academy. -### 1.1 主要功能 Main Features +**DFTT**是北京电影学院影视技术系(Department of Film and TV Technology)的缩写。 -- 支持多种时码格式输入,如SMPTE、SRT、DLP(Cine Canvas)、FFMPEG、FCPX、帧号、现实时间等。 +### Key Features | 主要特性 - Multiple timecode format support, including SMPTE, SRT, DLP(Cine Canvas), FFMPEG, FCPX, frame count, time, etc. +- **Multiple Format Support** - SMPTE, SRT, DLP (Cine Canvas), FFMPEG, FCPX, frame count, and timestamps -- 支持高帧率,目前支持0.01-999.99fps范围内的帧率。 + **多格式支持** - SMPTE、SRT、DLP(Cine Canvas)、FFMPEG、FCPX、帧计数和时间戳 - High frame rate support, currently supports frame range from 0.01 to 999.99fps. +- **High Frame Rate** - Supports 0.01 to 999.99 fps -- 支持严格的丢帧/非丢帧SMPTE格式。 + **高帧率** - 支持0.01至999.99 fps - Strictly support SMPTE DF/NDF format. +- **Drop-Frame Accurate** - Strict SMPTE drop-frame/non-drop-frame compliance -- 目前支持-99到99小时时间范围。 + **丢帧精确** - 严格遵循SMPTE丢帧/非丢帧标准 - Currently support time range from -99 to 99 hours. +- **High Precision** - Uses `fractions.Fraction` internally for lossless calculations -- 支持**严格**模式,在该模式下时码会在0-24小时范围内循环,任意超出该范围的时码会自动转换至范围内。 + **高精度** - 内部使用`fractions.Fraction`进行无损计算 - **Strict** Mode support, the timecode will circulate from 0 to 24 hours, any timecode outside this range will be automatically converted to a timecode inside it. +- **Rich Operators** - Full arithmetic (+, -, *, /) and comparison (==, !=, <, >, <=, >=) support -- 内部以高精度时间戳进行存储和计算,各类FPS转换、时码格式转换输出都能保持最高精度。 + **丰富的运算符** - 完整的算术和比较运算支持 - Uses high precision timestamp inside for storage and calculation, any FPS conversion or format conversion output can maintain their highest precision. +- **Strict Mode** - Optional 24-hour wrapping for broadcast workflows -- 常用运算符支持,包括时码与时码、时码与数字的各类加减乘除、比较运算。 + **严格模式** - 可选的24小时循环模式,适用于广播工作流 - Common operator support, including addition, subtraction, multiplication, division, and comparison operator between two timecode objects or a timecode object and a number. +## Installation | 安装 -## 2. 如何安装 How to install - -```python -python pip install dftt_timecode +```bash +pip install dftt_timecode ``` -### 2.1 包依赖 Package dependency +**Requirements:** Python 3.11 or higher -- fractions -- logging -- math -- functools -- re +**要求:** Python 3.11或更高版本 -## 3. 使用方法说明 How to use +## Quick Start | 快速入门 -### 3.1 导入 Import +### Basic Usage | 基本使用 ```python from dftt_timecode import DfttTimecode -``` - -### 3.2 新建时码类对象 Create timecode objects - -```Python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -#以SMPTE非丢帧时码新建对象 Create object using SMPTE NDF -a = DfttTimecode('1000f', 'auto', fps=119.88, drop_frame=True, strict=True) -#以帧数新建对象 Create object using frame count -a = DfttTimecode('3600.0s', 'auto', fps=Fraction(60000,1001), drop_frame=True, strict=True) -#以时间秒新建对象 Create object using time -a = DfttTimecode(-1200, 'auto', fps=23.976, drop_frame=False, strict=False) -#以int帧数新建对象 Create object using int frame count -``` - -对DfttTimecode()相关参数的详细说明,请查阅`4.1 DfttTimecode()参数说明`。 -For detailed parameters descriptions of DfttTimecode(), please refer to chapter `4.1 Parameters Descriptions of DfttTimecode()`. +# Create timecode from SMPTE format +tc = DfttTimecode('01:00:00:00', 'auto', fps=24) +print(tc) # (Timecode:01:00:00:00, Type:smpte, FPS:24.00 NDF, Strict) -### 3.3 操作时码类对象 Operate DfttTimecode objects +# Access timecode properties +print(tc.framecount) # 86400 +print(tc.timestamp) # 3600.0 +print(tc.fps) # 24 -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.type == 'smpte' -assert a.fps == 24 -assert a.framecount == 86400 -assert a.timestamp == 3600.0 -assert a.is_drop_frame == False -assert a.is_strict == True -assert a.timecode_output('smpte',output_part=0) == '01:00:00:00' -assert a.timecode_output('srt',output_part=1) == '01' - -a = DfttTimecode('25:00:01:103', 'auto', fps=120, drop_frame=False, strict=False) -a.set_fps(24) -assert a.fps == 24 -assert a.timecode_output('smpte') == '25:00:01:21' -a.set_strict(strict=True) -assert a.timecode_output('smpte') == '01:00:01:21' -a.set_strict(strict=False) -assert a.is_strict == False +# Convert to different formats +print(tc.timecode_output('smpte')) # 01:00:00:00 +print(tc.timecode_output('srt')) # 01:00:00,000 +print(tc.timecode_output('ffmpeg')) # 01:00:00.00 ``` -对时码类对象操作的详细说明,请查阅`4.2 时码类对象操作说明`。 - -For detailed descriptions of DfttTimecode objects' operations, please refer to chapter `4.2 Descriptions of DfttTimecode class operations`. -### 3.4 时码类运算符 Operators of DfttTimecode class +### Arithmetic Operations | 运算操作 ```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -b = DfttTimecode('01:12:34:12', 'auto', fps=24, drop_frame=False, strict=True) -print(a) # (Timecode:01:00:00:00, Type:smpte, FPS:24.00 NDF, Strict) -print(-a) # (Timecode:23:00:00:00, Type:smpte, FPS:24.00 NDF, Strict) -print(a + b) # (Timecode:02:12:34:12, Type:smpte, FPS:24.00 NDF, Strict) -print(a - b) # (Timecode:23:47:25:12, Type:smpte, FPS:24.00 NDF, Strict) -print(a * 2) # (Timecode:02:00:00:00, Type:smpte, FPS:24.00 NDF, Strict) -print(a / 2) # (Timecode:00:30:00:00, Type:smpte, FPS:24.00 NDF, Strict) -print(a == b) # False -print(a != b) # True -print(a > b) # False -print(a >= b) # False -print(a < b) # True -print(a <= b) # True -``` - -对时码类运算符的详细说明,请查阅`4.3 时码类运算符说明` - -For detailed descriptions of DfttTimecode's operators, please refer to chapter `4.3 Descriptions of DfttTimecode class operators`. - -## 4 参数详细说明 Detailed Parameters Descriptions - -### 4.1 DfttTimecode()参数说明 Parameters Descriptions of DfttTimecode() - -#### 4.1.1 参数一览 General Descriptions - -```python -a = DfttTimecode(timecode_value, timecode_type, fps, drop_frame, strict) -``` - -- **`timecode_value`** 是时码对象的时码值,可以是`str`、`int`、`float`、`tuple`、`list`、`Fraction`类型。 - - **`timecode_value`** is the value of a timecode, it can be a `str`, `int`, `float`, `tuple`, `list,` or a `Fraction`. - -- **`timecode_type`** 是时码对象的类型,是`str`类型,目前支持的时码类型包括`auto`、 `smpte`、 `srt`、 `ffmpeg`、 `fcpx`、 `frame`、 `time`。 - - **`timecode_type`** must be a `str`, currently supported timecode types include `auto`, `smpte`, `srt`, `ffmpeg`, `fcpx`, `frame`, `time`. - -- **`fps`** 是时码对象的帧率,可以是`int`、`float`、`Fraction`类型。 - - **`fps`** is the frame rate of the timecode object, can be an `int`, `float`, or a `Fraction`. - -- **`drop_frame`** 是时码对象的丢帧设置,是`bool`类型,只有当帧率存在丢帧格式时,这一设置才会生效,否则会强制将丢帧设为`False`。**`drop_frame `** 的默认值是`False`。 - - **`drop_frame`** must be a `bool`, a timecode object can only be drop-frameable under specific frame rate settings, if not so, **`drop_frame`** will be forced to `False`. The default value of **`drop_frame`** is `False`. - -- **`strict`** 为时码对象设置严格模式,是`bool`类型。设为`True`后,负值和超过24小时的时码都将被转换为0-24小时范围内的值,例如`25:00:00:00`将被转换为`01:00:00:00`, `-01:00:00:00`将被转换为`23:00:00:00`。 **`strict`** 的默认值是`True`。 - - **`strict`** will set the strict mode for a timecode object, it must be a `bool`. When set to `True`, negative timecode value and timecode value over 24 hours will be converted to a value inside the range 0 to 24 hours. For example, 25:00:00:00 will be converted to 01:00:00:00, -01:00:00:00 will be converted to 23:00:00:00. The default value of **`strict`** is `True`. -#### 4.1.2 timecode_value - -**`timecode_value`** 决定了时码对象的时间值,DfttTimecode支持以多种类型的数据初始化时间值,且都支持负数。下面详细列出了各个数据类型对应的(可选)初始化方式: - -**`timecode_value`** determines the actual time of a timecode object. DfttTimecode supports initialize time by different data types, including negative numbers. The following table lists different data types and their supported initialization methods. - -| 数据类型
Data type | 支持的初始化方式
Supported initialization methods | -| :---------------------: | :-------------------------------------------------------: | -| `str` | `auto`, `smpte`, `srt`, `ffmpeg`, `fcpx`, `frame`, `time` | -| `int` | `auto`, `frame`, `time` | -| `float` | `auto`, `time` | -| `tuple` | `auto`, `time` | -| `list` | `auto`, `time` | -| `fraction` | `auto`, `time` | - -目前,DfttTimecode不支持以小数为单位的帧计数方式。 - -Currently, DfttTimecode does not support frame count value in decimals. - -#### 4.1.3 timecode_type - -**`timecode_type`** 决定了时码对象的类型。DfttTimecode支持自动判断类型,也支持手动指定类型。在部分场景,如输入值是`int`类时,手动指定类型可以有效地区分以帧计数初始化时码和以时间初始化时码这两种行为。 - -**`timecode_type`** determines the timecode type of a timecode object. DfttTimecode supports auto-configure timecode type as well as manual assign a timecode type. Under some circumstances, for example, the input data is `int`, manual assign a timecode type is a sufficient way to clarify whether the input is intended to be a frame or a time value. - -下表列出了一系列样例 **`timecode_value`** 输入和他们在`'auto'`模式下对应的时码类型: - -The following sheet gives a list of example **`timecode_value`** input and their corresponding timecode type under `'auto'` mode. - -| timecode_value | auto模式下的type
Type under auto mode | 备注
Comment | -| :-------------------------------: | :----------------------------------------: | :----------------------------------------------------------: | -| `'01:00:00:00'` | `smpte` | **`drop_frame`** 将自动设为`False `
**`drop_frame`** will be set to `False` | -| `'01:00:00;00'`, `'01:00:00;000'` | `smpte` | **`drop_frame`** 将自动设为`True`
**`drop_frame`** will be set to `True` | -| `'01:00:00:000'` | `smpte` | 高帧率`smpte`时码,形式与`dlp`相近,如果输入值为`dlp`请强制指认 **`timecode_type`** 为`dlp`
High frame rate timecode, this format is similar to `dlp` timecode, so if your input timecode is actually in `dlp` format, please force **`timecode_type`** to `dlp` | -| `'01:00:00,000'` | `srt` | 最后三位表示毫秒
The last three digits represent milliseconds | -| `'01:00:00.00'` | `ffmpeg` | 最后两位表示秒的小数部分
The last two digits represent the decimal part of a second | -| `'1/24s'`, `'1/24'` | `fcpx` | 可以省略“s”
*s* can be omitted | -| `'1000f`, `'1000'` | `frame` | 可以省略“f”
*f* can be omitted | -| `’1000s'`,`'1000.0'`,`'1000.0s'` | `time` | 可以省略“s”
*s* can be omitted | -| `1000` | `frame` | `int` 数据会自动被认定为`frame`类
`int` data will be considered as a `frame` type | -| `1000.0` | `time` | `float` 数据会自动被认定为`time`类
`float` data will be considered as a `time` type | -| `[1000, 2000]` | `time` | 前者会成为`Fraction`的分子,后者成为分母
the former part will become the numerator of a `Fraction`, and the latter will become the dominator | -| `(1000, 2000)` | `time` | 前者会成为`Fraction`的分子,后者成为分母
the former part will become the numerator of a `Fraction`, and the latter will become the dominator | -| `Fraction(1000, 2000)` | `time` | 也可以直接传入一个`Fraction`对象
Just passing a `Fraction` object is also acceptable | - -如果输入的时码值与所选择的时码类型不匹配,会抛出错误。 - -If the input timecode value does not match the given timecode type, an error will be raised. - -#### 4.1.4 fps - -**`fps`** 是时码对象的帧率,可以是`int`、`float`、`Fraction`类型。 - -**`fps`** is the frame rate of the timecode object, can be an `int`, `float` or a `Fraction`. - -#### 4.1.5 drop_frame - -**`drop_frame`** 是时码对象的丢帧设置,是`bool`类型,只有当帧率存在丢帧格式时,这一设置才会生效,否则会强制将丢帧设为`False`。**`drop_frame `** 的默认值是`False`。 - -**`drop_frame`** must be a `bool`, a timecode object can only be drop-frameable under specific frame rate settings, if not so, **`drop_frame`** will be forced to `False`. The default value of **`drop_frame`** is `False`. - -当 **`timecode_type`** 为`auto`时,会根据输入数据的分隔符自动设置 **`drop_frame`** 。 - -When **`timecode_type`** is set to `auto`, **`drop_frame`** will be auto-set according to the separator of the input data. - -当 **`timecode_value`** 在当前 **`drop_frame`** 设置下不合法时(仅当 **`timecode_type`** 为`smpte`时会有这种情况),将会报错。 - -When **`timecode_value`** is illegal under the current **`drop_frame`** setting (this should only happen when **`timecode_type`** is `smpte`), there will be an error. - -#### 4.1.6 strict - -**`strict`** 为时码对象设置严格模式,是`bool`类型。设为`True`后,负值和超过24小时的时码都将被转换为0-24小时范围内的值,例如`25:00:00:00`将被转换为`01:00:00:00`, `-01:00:00:00`将被转换为`23:00:00:00`。**`strict`** 的默认值是`True`。 - -**`strict`** will set the strict mode for a timecode object, it must be a `bool`. When set to `True`, negative timecode value and timecode value over 24 hours will be converted to a value inside the range 0 to 24 hours. For example, 25:00:00:00 will be converted to 01:00:00:00, -01:00:00:00 will be converted to 23:00:00:00.The default value of **`strict`** is `True`. - -特别地,对于丢帧时码,由于严格模式的规则是不出现超过24:00:00:00的时码(实际上这个值会被转为00:00:00:00)。因此,在该模式下可容纳的总帧数会小于相同帧率的非丢帧时码。 - -In particular, as for a drop-frame timecode, the rule of strict mode does not allow a timecode value greater than 24:00:00:00 (actually, this value will be converted to 00:00:00:00). So, the maximum frame count number a drop-frame timecode can reach under strict mode will be less than a timecode with the same framerate but set to non-drop-frame mode. - -#### 4.1.7 补充说明 Additional info - -支持使用DfttTimecode对象初始化新DfttTimecode对象 - -Using a DfttTimecode object to instance a new DfttTimecode object. - -```python -tc_a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -tc_b = DfttTimecode(tc_a) -``` - -### 4.2 时码类对象操作说明 Descriptions of DfttTimecode class operations - -#### 4.2.1 `self.type` - -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.type == 'smpte' -``` - -返回DfttTimecode对象的 **`timecode_type`** 属性,返回类型为`str`。 - -Returns the **`timecode_type`** attribute of a DfttTimecode object, returned data type is `str`. - -#### 4.2.2 `self.fps` - -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.fps == 24 -``` - -返回DfttTimecode对象的 **`fps`** 属性,返回类型取决于设置fps所用的变量类型。 - -Returns the **`fps`** attribute of a DfttTimecode object, returned data type is determined by the data type used to set the **`fps`** attribute. - -#### 4.2.3 `self.framecount` - -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.framecount == 86400 -``` +from dftt_timecode import DfttTimecode -返回DfttTimecode对象从0时间起经过的总帧数,返回类型为`int`。 +a = DfttTimecode('01:00:00:00', fps=24) +b = DfttTimecode('00:30:00:00', fps=24) -Returns the total frame count from 0 of a DfttTimecode, returned data type is `int`. +# Timecode arithmetic +print(a + b) # 01:30:00:00 +print(a - b) # 00:30:00:00 +print(a * 2) # 02:00:00:00 +print(a / 2) # 00:30:00:00 -#### 4.2.4 `self.timestamp` +# Comparison +print(a > b) # True +print(a == b) # False -```python -a = DfttTimecode('01:00:00:01', 'auto', fps=24, drop_frame=False, strict=True) -assert a.timestamp == 3600.04167 +# Add frames (int) or seconds (float) +print(a + 24) # Adds 24 frames +print(a + 1.0) # Adds 1 second ``` -返回DfttTimecode对象从0时间起经过的总时长,返回类型为`float`,精度为5位小数。 - -Returns the total time elapsed from 0 of a DfttTimecode, returned data type is `float`, the precision of the returned value is 5 decimal places. - -#### 4.2.5 `self.is_drop_frame` +### Drop-Frame Timecode | 丢帧时码 ```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.is_drop_frame == False -``` - -返回DfttTimecode对象的 **`drop_frame`** 属性,返回类型为`bool`. - -Returns the **`drop_frame`** attribute of a DfttTimecode object, returned data type is `bool`. - -#### 4.2.6 `self.is_strict` - -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.is_strict == True -``` - -返回DfttTimecode对象的 **`strict`** 属性,返回类型为`bool`. - -Returns the **`strict`** attribute of a DfttTimecode object, returned data type is `bool`. +from dftt_timecode import DfttTimecode -#### 4.2.7 `self.precise_timestamp` +# Drop-frame timecode for 29.97 fps +df_tc = DfttTimecode('01:00:00;00', fps=29.97, drop_frame=True) +print(df_tc) # Automatically detects drop-frame from semicolon separator -```python -a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -assert a.precise_timestamp == 3600 +# Non-drop-frame +ndf_tc = DfttTimecode('01:00:00:00', fps=29.97, drop_frame=False) ``` -返回DfttTimecode对象的 **`precise_timestamp`** 属性,返回类型为`Fraction`. -Returns the **`precise_timestamp`** attribute of a DfttTimecode object, returned data type is `Fraction`. - - -#### 4.2.8 `self.set_fps()` +### Format Conversion | 格式转换 ```python -a = DfttTimecode('01:00:00:101', 'auto', fps=120, drop_frame=False, strict=True) -a.set_fps(24, rounding = True) -a.set_fps(120) -assert a.timecode_output('smpte') == '01:00:00:100' -``` - -该函数会更改DfttTimecode对象的帧率,并可以选择在更改帧率时是否取整。 - -This function will change the frame rate of a DfttTimecode object, you can choose whether or not to round the timecode value while changing the frame rate. - -`self.set_fps()`函数共有两个参数,分别是 **`dest_fps`** 和 **`rounding`** 。 - -There are two parameters of `self.set_fps()`, they are **`dest_fps`** and **`rounding`**. - -**`dest_fps`** 是帧率转换的目标帧率,可以是`int`、`float`、`Fraction`类型。 +from dftt_timecode import DfttTimecode -**`dest_fps`** is the target frame rate of this transform, it can be a,n `int`, `float`, or a `Fraction`. +tc = DfttTimecode('1000f', fps=24) # Create from frame count -**`rounding`** 决定了帧率转换过程中是否舍入时间戳以对齐帧,具体可以参考下面的示例代码。 +# Convert to different formats +tc.set_type('smpte') +print(tc.timecode_output()) # 00:00:41:16 -**`rounding`** determines whether to round the time stamp to align to the exact frame while converting the frame rate, you can refer to the following example code to see how it works. +tc.set_type('srt') +print(tc.timecode_output()) # 00:00:41,667 -```python -a = DfttTimecode('01:00:00:101', 'auto', fps=120, drop_frame=False, strict=True) -a.set_fps(24, rounding = False) -a.set_fps(120) -assert a.timecode_output('smpte') == '01:00:00:101' -a.set_fps(24, rounding = True) -a.set_fps(120) -assert a.timecode_output('smpte') == '01:00:00:100' +# Change frame rate +tc.set_fps(25, rounding=True) +print(tc.timecode_output('smpte')) # 00:00:40:00 ``` -#### 4.2.9 `self.set_type()` - -该函数会更改DfttTimecode对象的时码类型,并可以选择在更改类型时是否取整。 - -This function will change the timecode type of a DfttTimecode object, you can choose whether or not to round the timecode value while changing the timecode type. - -`self.set_type()`函数共有两个参数,分别是 **`dest_type`** 和 **`rounding`** 。 - -`self.set_type()` has two parameters, they are **`dest_type`** and **`rounding`** . - -**`dest_type`** 是时码类型转换的目标时码类型,可以是除`'auto'`以外的任何一个支持的时码类型。 - -**`dest_type`** is the target timecode type of this transform, it can be any supported timecode type except `'auto'`. - -**`rounding`** 决定了时码类型转换过程中是否舍入时间戳以对齐帧,具体可以参考下面的示例代码。 - -**`rounding`** determines whether to round the time stamp to align to the exact frame while converting the timecode type, you can refer to the following example code to see how it works. +### Using Convenience Aliases | 使用便捷别名 ```python -a = DfttTimecode('01:00:00,123', 'auto', fps=24) -assert a.type == 'srt' -a.set_type('smpte', rounding=True) -assert a.type == 'smpte' -assert a.timecode_output('srt') == '01:00:00,125' -``` +# Shorter aliases for quick coding +from dftt_timecode import dtc, dtr -#### 4.2.10 `self.set_strict()` - -```python -a = DfttTimecode('25:01:02:05', 'auto', fps=24, strict=False) -a.set_strict() -assert a.is_strict == True -assert a.timecode_output('smpte') == '01:01:02:05' -a.set_strict(strict=False) -assert a.is_strict == False +tc = dtc('01:00:00:00', fps=24) # dtc = DfttTimecode +print(tc.framecount) # 86400 ``` -该函数会更改DfttTimecode对象的strict模式布尔值。 +## Documentation | 文档 -This function will change the strict mode bool value of a DfttTimecode object. +For comprehensive documentation including detailed API reference, advanced usage, and examples: -`self.set_strict()` 只有一个参数,即 **`strict`** 。**`strict`** 的类型是`bool`,默认值为`True`。 +完整文档包括详细的API参考、高级用法和示例: -`self.set_strict()` has one parameter, which is **`strict`**. The data type of **`strict`** is `bool`, the default value of **`strict`** is `Ture`. +**📖 [View Full Documentation on GitHub Pages](https://owenyou.github.io/dftt_timecode/)** -#### 4.2.11 `self.timecode_output()` +### Documentation Contents | 文档内容 -```python -a = DfttTimecode('01:02:03:05', 'auto', fps=24) -assert a.timecode_output() == '01:02:03:05' -assert a.timecode_output('srt') == '01:02:03,208' -assert a.timecode_output('srt', output_part=1) == '01' -assert a.timecode_output('srt', output_part=2) == '02' -assert a.timecode_output('srt', output_part=3) == '03' -assert a.timecode_output('srt', output_part=4) == '208' -``` - -该函数会以指定类型和部分返回DfttTimecode对象的时码值,返回类型为`str`。 - -This function will return the timecode value of a DfttTimecode object in the given timecode type and partition number format, the returned data type is `str`. +- **API Reference** - Complete class and method documentation -`self.timecode_output()` 有两个参数,分别是 **`dest_type`** 和 **`output_part`** 。 + **API参考** - 完整的类和方法文档 -`self.timecode_output()` has two parameters, they are **`dest_type`** and **`output_part`** . +- **Advanced Usage** - TimeRange operations, precision handling, format conversion -**`dest_type`** 是输出时码的类型,可以是任何一个支持的时码类型,它的默认值是`'auto'`,此时会根据DfttTimecode对象自身的时码类型决定输出类型。 + **高级用法** - TimeRange操作、精度处理、格式转换 -**`dest_type`** is the type of the output timecode value, it can be any supported timecode type, the default value of it is `'auto'`, which means the function will determine the output timecode type according to the timecode type of the DfttTimecode object itself. +- **Examples** - Real-world usage patterns and best practices -**`output_part`** 是输出的部分,它应是一个`int`值。它的默认值是`0`,即完整输出。`1`到`4`依次代表输出从左至右的每个时码部分。 + **示例** - 实际应用模式和最佳实践 -**`output_part`** is the partition of the output timecode value, it is an `int`. The default value of it is 0, which means a complete output. Each of 1 to 4 represents an output timecode part from left to right. +- **Format Specifications** - Detailed format descriptions and validation rules -### 4.3 时码类运算符说明 Descriptions of DfttTimecode class operators + **格式规范** - 详细的格式描述和验证规则 -#### 4.3.1 `print(self)` +## TimeRange Support | 时间范围支持 -该运算符会打印DfttTimecode对象相关的时间码值,如下所示。 +The library includes `DfttTimeRange` for working with time intervals: -This operator will print timecode value of a DfttTimecode object, as the following codes show. +库中包含用于处理时间间隔的`DfttTimeRange`: ```python -a = DfttTimecode('01:00:00,123', 'srt', fps=24, drop_frame=False, strict=True) -print(a) # 01:00:00,123 -``` +from dftt_timecode import DfttTimeRange -#### 4.3.2 `-self` +# Create a time range +tr = DfttTimeRange( + start='01:00:00:00', + end='02:00:00:00', + fps=24 +) -该运算符会将DfttTimecode对象的时码值取负,且不改变其他属性,如下所示。 +print(tr.duration) # Duration timecode +print(tr.framecount) # Total frames in range -This operator will yield the negation of the timecode value of a DfttTimecode object, and won't affect any of the rest attributes, as the following codes show. +# TimeRange operations +tr1 = DfttTimeRange('01:00:00:00', '02:00:00:00', fps=24) +tr2 = DfttTimeRange('01:30:00:00', '02:30:00:00', fps=24) -```python -a = DfttTimecode('01:00:00,123', 'srt', fps=24, drop_frame=False, strict=True) -print(-a) # 22:59:59,877 +intersection = tr1.intersection(tr2) # Overlapping portion +union = tr1.union(tr2) # Combined range ``` -#### 4.3.3 `+` - -该运算符可以将两个DfttTimecode对象相加,或将DfttTimecode对象与`int`,`float`或`Fraction`相加。 - -This operator can add two DfttTimecode objects together, or add a DfttTimecode object with an `int`, `float`, or a `Fraction`. - -当DfttTimecode对象与`int`相加时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`相加时,后者的值将被当作时间戳处理。 - -When adding a DfttTimecode object with an `int`, the `int` will be considered as a frame number. When adding a DfttTimecode object with a `float` or a `Fraction`, the latter will be considered as a time stamp. - -相加的DfttTimecode对象必须拥有相同的帧率。 - -The two DfttTimecode objects to perform the addition must have the same frame rate. - -#### 4.3.4 `-` - -该运算符可以将两个DfttTimecode对象相减,或将DfttTimecode对象与`int`,`float`或`Fraction`相减。 - -This operator can perform a subtraction between two DfttTimecode objects, or perform a subtraction between a DfttTimecode object and an `int`, `float`, or a `Fraction`. - -当DfttTimecode对象与`int`相减时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`相加时,后者的值将被当作时间戳处理。 - -When performing a subtraction between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a subtraction between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. +See the [full documentation](https://owenyou.github.io/dftt_timecode/) for complete TimeRange API reference. -相减的DfttTimecode对象必须拥有相同的帧率。 +查看[完整文档](https://owenyou.github.io/dftt_timecode/)了解完整的TimeRange API参考。 -The two DfttTimecode objects to perform the subtraction must have the same frame rate. +## License | 许可证 -#### 4.3.5 `*` +This project is licensed under the LGPL 2.1 License - see the [LICENSE](LICENSE) file for details. -该运算符可以将一个DfttTimecode对象与一个`int`,`float`或`Fraction`相乘,后者的数学意义是倍数。 +本项目采用LGPL 2.1许可证 - 详见[LICENSE](LICENSE)文件。 -This operator can perform a multiplication between a DfttTimecode object and an `int`, `float`, or a `Fraction`, the mathematical meaning of the latter is a factor. +## Contributing | 贡献 -#### 4.3.6 `/` +Contributions are welcome! Please feel free to submit a Pull Request. -该运算符可以将一个DfttTimecode对象与一个`int`,`float`或`Fraction`相除,后者的数学意义是倍数。 +欢迎贡献!请随时提交Pull Request。 -This operator can perform a division between a DfttTimecode object and an `int`, `float`, or a `Fraction`, the mathematical meaning of the latter is a factor. - -需要注意的是,只有当DfttTimecode对象作为被除数时,除法运算才是有意义的,DfttTimecode对象不能作除数。 - -Please be noted, the division operation only makes sense when the DfttTimecode object is used as the dividend, the DfttTimecode object cannot be used as a divisor. - -#### 4.3.7 `==` - -该运算符可以比较两个DfttTimecode对象是否相等,或比较DfttTimecode对象和`int`,`float`或`Fraction`是否相等。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell whether they are equal to each other. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.8 `!=` - -该运算符可以比较两个DfttTimecode对象是否相等,或比较DfttTimecode对象和`int`,`float`或`Fraction`是否相等。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell whether they are equal to each other. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.9 `>` - -该运算符可以比较两个DfttTimecode对象的大小,或比较DfttTimecode对象和`int`,`float`或`Fraction`的大小。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell which one is the greater one. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.10 `>=` - -该运算符可以比较两个DfttTimecode对象的大小,或比较DfttTimecode对象和`int`,`float`或`Fraction`的大小。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell which one is the greater one. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.11 `<` - -该运算符可以比较两个DfttTimecode对象的大小,或比较DfttTimecode对象和`int`,`float`或`Fraction`的大小。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell which one is the greater one. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.12 `<=` - -该运算符可以比较两个DfttTimecode对象的大小,或比较DfttTimecode对象和`int`,`float`或`Fraction`的大小。 - -This operator can perform a comparison between two DfttTimecode objects, or perform a comparison between a DfttTimecode object and an `int`, `float`, or a `Fraction`, to tell which one is the greater one. - -当两个DfttTimecode对象作比较时,将比较二者的时间戳。当DfttTimecode对象与`int`作比较时,`int`值将被当作帧计数处理。当DfttTimecode对象与`float`或`Fraction`作比较时,后者的值将被当作时间戳处理。 - -When performing a comparison between two DfttTimecode objects, a comparison of their timestamp will be performed. When performing a comparison between a DfttTimecode object and an `int`, the `int` will be considered as a frame number. When performing a comparison between a DfttTimecode object and a `float` or a `Fraction`, the latter will be considered as a time stamp. - -#### 4.3.13 `float(self)` - -返回 `self.timestamp` 的值.参考[self.timestamp](#424-selftimestamp) - -Return value of `self.timestamp`.Reference to [self.timestamp](#424-selftimestamp) - -```python -tc_a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -float(tc_a) -#36000.0 -``` - -#### 4.3.14 `int(self)` - -返回 `self.framecount` 的值.参考[self.framecount](#423-selfframecount) - -Return value of `self.framecount`.Reference to [self.framecount](#423-selfframecount) - -```python -tc_a = DfttTimecode('01:00:00:00', 'auto', fps=24, drop_frame=False, strict=True) -int(tc_a) -#864000 -``` +## Links | 链接 -#TODO TimeRange readme \ No newline at end of file +- **PyPI:** https://pypi.org/project/dftt-timecode/ +- **Documentation:** https://owenyou.github.io/dftt_timecode/ +- **Source Code:** https://github.com/OwenYou/dftt_timecode +- **Issue Tracker:** https://github.com/OwenYou/dftt_timecode/issues