diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..cf33297 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Lint and Test + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4.3.0 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml new file mode 100644 index 0000000..228376f --- /dev/null +++ b/.github/workflows/pythonpublish.yml @@ -0,0 +1,26 @@ +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_APIKEY }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/.gitignore b/.gitignore index c06b70f..9b7c08b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,13 @@ # Vim Swapfiles *.swp +.DS_Store + +venv/ + +.coverage +lcov.info + # venv venv/ - -.DS_Store -.coverage -lcov.info \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d23d520..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: python -python: - - "3.6" - - "3.5" -script: - - "python3 setup.py test" -install: - - "pip3 install setuptools" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..edce44d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +Contributions to this project are welcome! + +The best way to contribute code to this project is to find this project on [Github][github] and submit a pull request. + +## Call for EDLs + +If you have EDLs you are having trouble working with becuase of unusual formatting, please send me a copy! Contact us +through [Github]. + + +[github]: https://github.com/iluvcapra/pycmx diff --git a/LICENSE b/LICENSE index 410fc08..982cef7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018 Jamie Hardt. +Copyright (c) 2022 Jamie Hardt. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0041a1c..c41dd81 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -[![Build Status](https://travis-ci.com/iluvcapra/pycmx.svg?branch=master)](https://travis-ci.com/iluvcapra/pycmx) [![Documentation Status](https://readthedocs.org/projects/pycmx/badge/?version=latest)](https://pycmx.readthedocs.io/en/latest/?badge=latest) ![](https://img.shields.io/github/license/iluvcapra/pycmx.svg) ![](https://img.shields.io/pypi/pyversions/pycmx.svg) [![](https://img.shields.io/pypi/v/pycmx.svg)](https://pypi.org/project/pycmx/) ![](https://img.shields.io/pypi/wheel/pycmx.svg) +![GitHub last commit](https://img.shields.io/github/last-commit/iluvcapra/pycmx) +[![Lint and Test](https://github.com/iluvcapra/pycmx/actions/workflows/python-package.yml/badge.svg)](https://github.com/iluvcapra/pycmx/actions/workflows/python-package.yml) # pycmx @@ -83,10 +84,4 @@ Audio channel 7 is present False ``` -## Should I Use This? -At this time, this is (at best) beta software. I feel like the interface is -about where where I'd like it to be but more testing is required. - -Contributions are welcome and will make this module production-ready all the -faster! Please reach out or file a ticket! diff --git a/docs/CMX3600.pdf b/docs/CMX3600.pdf deleted file mode 100644 index b078faf..0000000 Binary files a/docs/CMX3600.pdf and /dev/null differ diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 543c6b1..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/source/classes.rst b/docs/source/classes.rst new file mode 100644 index 0000000..26d9f2c --- /dev/null +++ b/docs/source/classes.rst @@ -0,0 +1,24 @@ +pycmx Classes +============= + +.. autoclass:: pycmx.edit_list.EditList + :members: + + +.. autoclass:: pycmx.event.Event + :members: + + +.. autoclass:: pycmx.edit.Edit + :members: + + +.. autoclass:: pycmx.transition.Transition + :members: + + +.. autoclass:: pycmx.channel_map.ChannelMap + :members: + + + diff --git a/docs/source/conf.py b/docs/source/conf.py index 3b97388..708e879 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,13 +14,13 @@ # import os import sys -sys.path.insert(0, os.path.abspath('../../pycmx')) - +sys.path.insert(0, os.path.abspath('../..')) +print(sys.path) # -- Project information ----------------------------------------------------- project = u'pycmx' -copyright = u'2018, Jamie Hardt' +copyright = u'2022, Jamie Hardt' author = u'Jamie Hardt' # The short X.Y version @@ -63,7 +63,7 @@ master_doc = 'index' # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'em' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/docs/source/function.rst b/docs/source/function.rst new file mode 100644 index 0000000..25137be --- /dev/null +++ b/docs/source/function.rst @@ -0,0 +1,7 @@ +Parse Function +============== + + +.. autofunction:: pycmx.parse_cmx_events.parse_cmx3600 + + diff --git a/docs/source/index.rst b/docs/source/index.rst index 6cacb23..d86552d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -8,13 +8,13 @@ Welcome to pycmx's documentation! .. toctree:: :maxdepth: 5 - :caption: API Reference: - - pycmx + :caption: API Reference + + function + classes Indices and tables ================== * :ref:`genindex` -* :ref:`modindex` * :ref:`search` diff --git a/docs/source/modules.rst b/docs/source/modules.rst deleted file mode 100644 index 7013fac..0000000 --- a/docs/source/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -pycmx -===== - -.. toctree:: - :maxdepth: 4 - - pycmx diff --git a/docs/source/pycmx.rst b/docs/source/pycmx.rst deleted file mode 100644 index 9a87ab5..0000000 --- a/docs/source/pycmx.rst +++ /dev/null @@ -1,46 +0,0 @@ -pycmx package -============= - -Submodules ----------- - -pycmx.channel\_map module -------------------------- - -.. automodule:: pycmx.channel_map - :members: - :undoc-members: - :show-inheritance: - -pycmx.parse\_cmx\_events module -------------------------------- - -.. automodule:: pycmx.parse_cmx_events - :members: - :undoc-members: - :show-inheritance: - -pycmx.parse\_cmx\_statements module ------------------------------------ - -.. automodule:: pycmx.parse_cmx_statements - :members: - :undoc-members: - :show-inheritance: - -pycmx.util module ------------------ - -.. automodule:: pycmx.util - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pycmx - :members: - :undoc-members: - :show-inheritance: diff --git a/pycmx/__init__.py b/pycmx/__init__.py index 6e06917..387b58d 100644 --- a/pycmx/__init__.py +++ b/pycmx/__init__.py @@ -8,7 +8,7 @@ copy and reuse this software, refer to the LICENSE file included with the distribution. """ -__version__ = '1.0' +__version__ = '1.1.1' __author__ = 'Jamie Hardt' from .parse_cmx_events import parse_cmx3600 diff --git a/pycmx/channel_map.py b/pycmx/channel_map.py index fea8d3c..a7071ef 100644 --- a/pycmx/channel_map.py +++ b/pycmx/channel_map.py @@ -2,13 +2,14 @@ # (c) 2018 Jamie Hardt from re import (compile, match) +from typing import Dict, Tuple class ChannelMap: """ Represents a set of all the channels to which an event applies. """ - _chan_map = { + _chan_map : Dict[str, Tuple] = { "V" : (True, False, False), "A" : (False, True, False), "A2" : (False, False, True), @@ -27,6 +28,11 @@ class ChannelMap: 'True if video is included' return self.v + @property + def audio(self): + 'True if an audio channel is included' + return len(self._audio_channel_set) > 0 + @property def channels(self): 'A generator for each audio channel' @@ -35,7 +41,7 @@ class ChannelMap: @property def a1(self): - """True if A1 is included.""" + """True if A1 is included""" return self.get_audio_channel(1) @a1.setter @@ -44,7 +50,7 @@ class ChannelMap: @property def a2(self): - """True if A2 is included.""" + """True if A2 is included""" return self.get_audio_channel(2) @a2.setter @@ -53,7 +59,7 @@ class ChannelMap: @property def a3(self): - """True if A3 is included.""" + """True if A3 is included""" return self.get_audio_channel(3) @a3.setter @@ -62,7 +68,7 @@ class ChannelMap: @property def a4(self): - """True if A4 is included.""" + """True if A4 is included""" return self.get_audio_channel(4) @a4.setter @@ -70,18 +76,18 @@ class ChannelMap: self.set_audio_channel(4,val) def get_audio_channel(self,chan_num): - """True if chan_num is included.""" + """True if chan_num is included""" return (chan_num in self._audio_channel_set) def set_audio_channel(self,chan_num,enabled): - """If enabled is true, chan_num will be included.""" + """If enabled is true, chan_num will be included""" if enabled: self._audio_channel_set.add(chan_num) elif self.get_audio_channel(chan_num): self._audio_channel_set.remove(chan_num) def _append_event(self, event_str): - alt_channel_re = compile('^A(\d+)') + alt_channel_re = compile(r'^A(\d+)') if event_str in self._chan_map: channels = self._chan_map[event_str] self.v = channels[0] @@ -93,14 +99,15 @@ class ChannelMap: self.set_audio_channel(int( matchresult.group(1)), True ) def _append_ext(self, audio_ext): - self.a3 = ext.audio3 - self.a4 = ext.audio4 + self.a3 = audio_ext.audio3 + self.a4 = audio_ext.audio4 def __or__(self, other): """ - Return the logical union of this channel map with another + the logical union of this channel map with another """ out_v = self.video | other.video out_a = self._audio_channel_set | other._audio_channel_set return ChannelMap(v=out_v,audio_channels = out_a) + diff --git a/pycmx/edit_list.py b/pycmx/edit_list.py index 8a7908d..a8c27f8 100644 --- a/pycmx/edit_list.py +++ b/pycmx/edit_list.py @@ -53,10 +53,8 @@ class EditList: @property def title(self): """ - The title of this edit list, as attensted by the 'TITLE:' statement on - the first line. + The title of this edit list. """ - 'The title of the edit list' return self.title_statement.title diff --git a/pycmx/parse_cmx_events.py b/pycmx/parse_cmx_events.py index f5c6549..211e2ad 100644 --- a/pycmx/parse_cmx_events.py +++ b/pycmx/parse_cmx_events.py @@ -14,7 +14,7 @@ def parse_cmx3600(f): f : a file-like object, anything that's readlines-able. Returns: - An :class:`EditList`. + An :class:`pycmx.edit_list.EditList`. """ statements = parse_cmx3600_statements(f) return EditList(statements) diff --git a/pycmx/transition.py b/pycmx/transition.py index 815f7d0..988b411 100644 --- a/pycmx/transition.py +++ b/pycmx/transition.py @@ -70,12 +70,12 @@ class Transition: @property def key_background(self): "`True` if this edit is a key background." - return self.transition == KeyBackground + return self.transition == Transition.KeyBackground @property def key_foreground(self): "`True` if this edit is a key foreground." - return self.transition == Key + return self.transition == Transition.Key @property def key_out(self): @@ -83,4 +83,4 @@ class Transition: `True` if this edit is a key out. This material will removed from the key foreground and replaced with the key background. """ - return self.transition == KeyOut + return self.transition == Transition.KeyOut diff --git a/pypi_upload.sh b/pypi_upload.sh deleted file mode 100755 index 21ed0a3..0000000 --- a/pypi_upload.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -python3 -m twine upload --repository-url https://upload.pypi.org/legacy/ dist/* diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9d159f2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +attrs==22.1.0 +coverage==6.5.0 +exceptiongroup==1.0.4 +iniconfig==1.1.1 +packaging==21.3 +pluggy==1.0.0 +pyparsing==3.0.9 +pytest==7.2.0 +tomli==2.0.1 diff --git a/setup.py b/setup.py index 928dca6..f8a97aa 100644 --- a/setup.py +++ b/setup.py @@ -4,20 +4,30 @@ with open("README.md", "r") as fh: long_description = fh.read() setup(name='pycmx', - version='1.0', + version='1.1.4', author='Jamie Hardt', author_email='jamiehardt@me.com', description='CMX 3600 Edit Decision List Parser', long_description_content_type="text/markdown", long_description=long_description, + project_urls={ + 'Source': + 'https://github.com/iluvcapra/pycmx', + 'Documentation': + 'https://pycmx.readthedocs.io/', + 'Issues': + 'https://github.com/iluvcapra/pycmx/issues', + }, url='https://github.com/iluvcapra/pycmx', classifiers=['Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: MIT License', 'Topic :: Multimedia', 'Topic :: Multimedia :: Video', 'Topic :: Text Processing', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6' + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10' ], packages=['pycmx']) diff --git a/tests/test_parse.py b/tests/test_parse.py index b5ea2e8..4ae89b5 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -66,6 +66,7 @@ class TestParse(TestCase): self.assertFalse( events[0].edits[0].channels.a1) self.assertTrue( events[0].edits[0].channels.a2) self.assertTrue( events[2].edits[0].channels.get_audio_channel(7) ) + self.assertTrue( events[2].edits[0].channels.audio) def test_multi_edit_events(self):