119 Commits

Author SHA1 Message Date
Jamie Hardt d0e2e5bbe5 Update pythonpublish.yml
Changed action to release-published
2023-05-17 17:18:36 -07:00
Jamie Hardt e90897e503 Update pythonpublish.yml 2023-05-17 17:06:17 -07:00
Jamie Hardt 4daa008e91 Merge branch 'master' into release 2023-05-17 17:03:47 -07:00
Jamie Hardt cf1d71800e Update pythonpublish.yml 2023-05-17 17:01:25 -07:00
Jamie Hardt fcb25ae183 Update pythonpublish.yml
Updated action versions
2023-05-17 16:59:17 -07:00
Jamie Hardt 97ada84bfe Merge pull request #7 from iluvcapra/release
Release
2023-05-17 16:55:00 -07:00
Jamie Hardt d157512c32 Update pythonpublish.yml
Updating checkoutv2
2023-05-17 16:54:00 -07:00
Jamie Hardt 1e2d31716e Merge remote-tracking branch 'refs/remotes/origin/master' 2023-05-17 16:50:41 -07:00
Jamie Hardt 6be7c54de5 Update pythonpublish.yml 2023-05-17 16:48:25 -07:00
Jamie Hardt a3109cdb7a Merge branch 'master' into release 2023-05-17 16:30:36 -07:00
Jamie Hardt 3a9d81417e Removing 3.6 from test grid and project classifiers
3.6 isn't supported by the github actions anymore.
2023-05-17 16:24:36 -07:00
Jamie Hardt 9838e6b357 Update python-package.yml
Adding py3.6 to test grid
2023-05-17 16:22:50 -07:00
Jamie Hardt 41fdeeaf56 Update python-package.yml
Adding 3.11 to test grid
2023-05-17 16:21:06 -07:00
Jamie Hardt b3b6e57f6c Update pyproject.toml
Adding 3.11 to versions
2023-05-17 16:20:32 -07:00
Jamie Hardt 62e7f10cf4 Update pyproject.toml
Downgraded minimum python version
2023-05-17 16:19:02 -07:00
Jamie Hardt f855d3581d Update python-package.yml
Fixed typo
2023-05-17 16:16:35 -07:00
Jamie Hardt 0b5555d333 Updated workflow for pyproject installation 2023-05-17 16:15:02 -07:00
Jamie Hardt 7123a6f1bb Deleted requirements.txt 2023-05-17 16:13:29 -07:00
Jamie Hardt 173493a610 Updated project to pyproject-style package 2023-05-17 16:12:42 -07:00
Jamie Hardt d20d9d1bdd Update pythonpublish.yml 2022-11-21 20:29:33 -08:00
Jamie Hardt d328e12283 Merge pull request #6 from iluvcapra/master
Update release branch
2022-11-18 23:06:52 -08:00
Jamie Hardt 4d83f81fc8 Merge pull request #5 from iluvcapra/release
Release
2022-11-18 23:03:21 -08:00
Jamie Hardt 69b6c7236d Update setup.py
Bump version number
2022-11-18 23:01:57 -08:00
Jamie Hardt 71ffe8cd0d Merge branch 'master' into release 2022-11-18 22:49:45 -08:00
Jamie Hardt 3fff6c8d2a Update gitignore 2022-11-18 22:47:44 -08:00
Jamie Hardt e350565430 Update channel_map.py 2022-11-18 22:37:29 -08:00
Jamie Hardt 9ec30ede02 Update channel_map.py 2022-11-18 22:34:37 -08:00
Jamie Hardt e5130b8011 Update edit_list.py 2022-11-18 22:33:33 -08:00
Jamie Hardt 00eaccabac Update index.rst
Twiddle
2022-11-18 22:27:20 -08:00
Jamie Hardt 521d86e444 Update conf.py 2022-11-16 23:24:52 -08:00
Jamie Hardt 0fdba2408b Create CONTRIBUTING.md 2022-11-16 21:26:38 -08:00
Jamie Hardt 199bba2466 Update setup.py 2022-11-16 21:18:27 -08:00
Jamie Hardt d8f0b5694e Update setup.py 2022-11-16 21:18:14 -08:00
Jamie Hardt 742b0f96c3 Delete .idea directory 2022-11-16 21:15:21 -08:00
Jamie Hardt d44948bdc1 Update LICENSE 2022-11-16 21:14:36 -08:00
Jamie Hardt 93cff15446 Update README.md 2022-11-16 21:04:33 -08:00
Jamie Hardt fc914409ce Update README.md 2022-11-16 21:04:13 -08:00
Jamie Hardt 770e7f45a4 Fixed doc generation 2022-11-16 19:00:17 -08:00
Jamie Hardt d3cf5fa5f2 Doc work 2022-11-16 18:47:16 -08:00
Jamie Hardt 67d2bd7093 Update classes.rst 2022-11-16 18:21:48 -08:00
Jamie Hardt 26a7eae437 Docs 2022-11-16 18:19:55 -08:00
Jamie Hardt 407aa1c1fd Update classes.rst 2022-11-16 18:15:58 -08:00
Jamie Hardt 13d0a80a10 Update classes.rst 2022-11-16 18:13:16 -08:00
Jamie Hardt dcd2a22a43 Update classes.rst 2022-11-16 18:09:30 -08:00
Jamie Hardt c586740269 Update classes.rst 2022-11-16 18:07:32 -08:00
Jamie Hardt e28dbbbe5e Update classes.rst 2022-11-16 18:06:29 -08:00
Jamie Hardt 41df450452 Update classes.rst 2022-11-16 18:05:19 -08:00
Jamie Hardt c37464036d Delete pycmx.rst 2022-11-16 18:04:25 -08:00
Jamie Hardt 9d89834eb3 Update index.rst 2022-11-16 18:02:15 -08:00
Jamie Hardt 8b53d2249c Docs 2022-11-16 18:00:53 -08:00
Jamie Hardt 0cdbc4e9be Docs 2022-11-16 17:59:08 -08:00
Jamie Hardt e229e807b1 Update index.rst 2022-11-16 17:55:22 -08:00
Jamie Hardt a7ee1f6737 Merge branch 'master' of https://github.com/iluvcapra/pycmx 2022-11-16 17:53:35 -08:00
Jamie Hardt 9097de8efa Delete modules.rst 2022-11-16 17:53:33 -08:00
Jamie Hardt bc6d7f34c0 Update python-package.yml
Removed 3.6 from grid
2022-11-16 17:10:00 -08:00
Jamie Hardt b642f859f3 Put it back 2022-11-16 17:09:22 -08:00
Jamie Hardt 29a9a5fba7 Removed coverage from reqs 2022-11-16 17:05:09 -08:00
Jamie Hardt 20ff7d7ee8 Added requirements.txt file 2022-11-16 17:03:44 -08:00
Jamie Hardt 183f121cfc Update channel_map.py
Added some typing
2022-11-16 16:57:21 -08:00
Jamie Hardt e2dffcb745 Update .gitignore 2022-11-16 16:42:12 -08:00
Jamie Hardt 8c2ba3cc09 Update README.md
Added Lint and Test badge
2022-11-16 16:25:08 -08:00
Jamie Hardt c7569045c1 Update python-package.yml
Named Package workflow
2022-11-16 16:24:16 -08:00
Jamie Hardt 50bcac23bb Update README.md
Removed travis badge.
2022-11-14 11:09:04 -08:00
Jamie Hardt 67b1631ba9 Update README.md
Removed codecov badge
2022-11-14 11:08:31 -08:00
Jamie Hardt 85cbafba8f Deleted .travis file 2022-11-13 17:57:06 -08:00
Jamie Hardt 595cf35e57 Update setup.py
Version 1.1.4
2022-11-13 17:53:51 -08:00
Jamie Hardt 7fa22d4b85 Update setup.py
Version 1.1.2
2022-11-13 17:50:26 -08:00
Jamie Hardt 42f2de54b5 Update README.md
Removed "Platform Lifecycle" section, this work is done
2022-11-13 17:42:46 -08:00
Jamie Hardt f7d1432014 Update pythonpublish.yml 2022-11-13 17:41:55 -08:00
Jamie Hardt db4eadb73e Update python-package.yml
Updated actions/setup-python version pin
2022-11-13 17:37:50 -08:00
Jamie Hardt 3305bc7920 Removing Python 3.5 from support 2022-11-13 17:33:57 -08:00
Jamie Hardt 6ba77b3568 Adding newer python versions
to setup.rb and test grid
2022-11-13 17:32:30 -08:00
Jamie Hardt c68f8bca80 Fixed spelling of an re 2022-11-13 17:29:10 -08:00
Jamie Hardt 284267c9c0 Fixed some bugs picked up in flake8 2022-11-13 17:25:56 -08:00
Jamie Hardt bd196f2dbf Create python-package.yml 2022-11-13 17:21:54 -08:00
Jamie Hardt b14a9a6319 Update .gitignore 2022-01-16 17:16:14 -08:00
Jamie Hardt 0cbd01f418 Update README.md 2020-01-05 14:55:43 -08:00
Jamie Hardt 50d48708e9 .idea files 2020-01-04 22:38:25 -08:00
Jamie Hardt f67c4ac2c5 Update setup.py 2020-01-04 22:38:15 -08:00
Jamie Hardt 1b8a3c3288 Create pythonpublish.yml 2020-01-04 22:36:54 -08:00
Jamie Hardt b37b57d7c9 Removed pypi upload code 2020-01-04 22:36:24 -08:00
Jamie Hardt a9937683e5 Update setup.py 2020-01-03 09:43:38 -08:00
Jamie Hardt 4fae65fa8d Update .travis.yml
Adding python 3.8
2020-01-03 09:42:40 -08:00
Jamie Hardt 566e6257f4 Added 'audio' method to ChannelMap
Added `audio` property to channelmap to test if it contains any audio channels.
2019-08-18 10:57:16 -07:00
Jamie Hardt c56d2066ad Update setup.py
v1.0.1
2019-08-17 13:06:39 -07:00
Jamie Hardt 8b49a788ae Add version 3.7 support checks 2019-08-17 13:01:51 -07:00
Jamie Hardt b31450f03d Update README.md
codecov badge
2019-01-05 12:57:16 -08:00
Jamie Hardt 5d14c3177a Update .travis.yml 2019-01-05 12:55:09 -08:00
Jamie Hardt 08dea6031d Update .travis.yml
switch to codecov
2019-01-05 12:49:52 -08:00
Jamie Hardt d23fa33558 Update setup.py
Added version 2.7
2019-01-04 19:06:46 -08:00
Jamie Hardt fcc4732d1a Update .travis.yml 2019-01-04 18:14:00 -08:00
Jamie Hardt 47c1ad96f0 Update .travis.yml 2019-01-04 18:01:49 -08:00
Jamie Hardt 804f649570 Configuring Coveralls 2019-01-04 18:01:23 -08:00
Jamie Hardt 58483198c3 Update .travis.yml 2019-01-04 09:12:21 -08:00
Jamie Hardt aa01e9ad2d Update .travis.yml 2019-01-03 22:03:23 -08:00
Jamie Hardt 464052f510 Update .travis.yml
Adding 2.7 to see what happens
2019-01-03 20:09:39 -08:00
Jamie Hardt 3ba28a61dd Update README.md
Removed "should I use this?"
2019-01-03 19:47:38 -08:00
Jamie Hardt b80339267a Version 1.0 2019-01-03 19:40:03 -08:00
Jamie Hardt 27d1073f8c Update test_parse.py 2019-01-03 19:35:20 -08:00
Jamie Hardt 0840ade312 Update test_parse.py
One more...
2019-01-03 19:33:26 -08:00
Jamie Hardt a52e4329ce Update test_parse.py 2019-01-03 19:31:33 -08:00
Jamie Hardt 47772b21d0 Merge branch 'master' of https://github.com/iluvcapra/pycmx 2019-01-03 19:28:50 -08:00
Jamie Hardt 7b4a76448e Changed a format string in the tests so 3.5 should build 2019-01-03 19:28:48 -08:00
Jamie Hardt c6c5d15e09 Update README.md 2019-01-03 11:13:31 -08:00
Jamie Hardt c439ea1fbe Update README.md 2019-01-03 11:12:52 -08:00
Jamie Hardt 68f118ab6b Update README.md
Removed Travis badge
2019-01-01 12:25:36 -08:00
Jamie Hardt a20491297e Update README.md 2018-12-31 12:11:17 -08:00
Jamie Hardt 9d57c2d374 Update README.md 2018-12-31 12:03:37 -08:00
Jamie Hardt 1882cc5308 Can't support 3.7 yet? 2018-12-31 11:59:59 -08:00
Jamie Hardt c57fe94335 Removed versions I don't support 2018-12-31 11:58:04 -08:00
Jamie Hardt 007661ef38 Merge branch 'master' of https://github.com/iluvcapra/pycmx 2018-12-31 11:52:23 -08:00
Jamie Hardt f34c6dd4db Update .travis.yml
Added more versions to travis
2018-12-31 11:52:08 -08:00
Jamie Hardt eb89708bab Update README.md
badges
2018-12-31 11:49:07 -08:00
Jamie Hardt 9fde608fa0 Update setup.py
Added version classifiers
2018-12-31 11:47:34 -08:00
Jamie Hardt 4c4ca428f2 Update README.md
Badges
2018-12-31 11:43:59 -08:00
Jamie Hardt fe4e3b9d85 Update README.md 2018-12-31 11:35:34 -08:00
Jamie Hardt 119467a884 Update README.md
Added pypi badge
2018-12-31 11:35:11 -08:00
Jamie Hardt b5a3285e64 Nudge version
Also saved upload command to a script
2018-12-29 16:31:48 -08:00
Jamie Hardt af1c532a67 Some SourceUMID Impl 2018-12-29 15:16:26 -08:00
25 changed files with 313 additions and 189 deletions
+41
View File
@@ -0,0 +1,41 @@
# 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", "3.11"]
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
python3 -m pip install -e .
# 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
+38
View File
@@ -0,0 +1,38 @@
name: Upload Python Package
on:
release:
types: [published]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.2
- name: Set up Python
uses: actions/setup-python@v4.6.0
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools build wheel twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_APIKEY }}
run: |
python -m build .
twine upload dist/*
- name: Report to Mastodon
uses: cbrgm/mastodon-github-action@v1.0.1
with:
message: |
I just released a new version of pycmx, my library for reading CMX EDLs!
#sounddesign #filmmaking #python
${{ github.server_url }}/${{ github.repository }}
env:
MASTODON_URL: ${{ secrets.MASTODON_URL }}
MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
+10
View File
@@ -10,3 +10,13 @@
# Vim Swapfiles # Vim Swapfiles
*.swp *.swp
.DS_Store
venv/
.coverage
lcov.info
# venv
venv/
-7
View File
@@ -1,7 +0,0 @@
language: python
python:
- "3.6"
script:
- "python3 setup.py test"
install:
- "pip3 install setuptools"
+13
View File
@@ -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
+1 -1
View File
@@ -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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+3 -29
View File
@@ -1,4 +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) [![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 # pycmx
@@ -82,32 +84,4 @@ Audio channel 7 is present
False False
``` ```
## How is this different from `python-edl`?
There are two important differences between `import edl` and `import pycmx`
and motivated my development of this module.
1. The `pycmx` parser doesn't take timecode or framerates into account,
and strictly treats timecodes like opaque values. As far as `pycmx` is
concerend, they're just strings. This was done because in my experience,
the frame rate of an EDL is often difficult to precisely determine and
often the frame rate of various sources is different from the frame rate
of the target track.
In any event, timecodes in an EDL are a kind of *address* and are not
exactly scalar, they're meant to point to a particular block of video or
audio data on a medium and presuming that they refer to a real time, or
duration, or are convertible, etc. isn't always safe.
2. The `pycmx` parser reads event numbers and keeps track of which EDL rows
are meant to happen "at the same time," with two decks. This makes it
easier to reconstruct transition A/B clips, and read clip names from
such events appropriately.
## 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!
BIN
View File
Binary file not shown.
-35
View File
@@ -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
+24
View File
@@ -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:
+4 -4
View File
@@ -14,13 +14,13 @@
# #
import os import os
import sys import sys
sys.path.insert(0, os.path.abspath('../../pycmx')) sys.path.insert(0, os.path.abspath('../..'))
print(sys.path)
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
project = u'pycmx' project = u'pycmx'
copyright = u'2018, Jamie Hardt' copyright = u'2022, Jamie Hardt'
author = u'Jamie Hardt' author = u'Jamie Hardt'
# The short X.Y version # The short X.Y version
@@ -63,7 +63,7 @@ master_doc = 'index'
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # 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 # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
+7
View File
@@ -0,0 +1,7 @@
Parse Function
==============
.. autofunction:: pycmx.parse_cmx_events.parse_cmx3600
+3 -3
View File
@@ -8,13 +8,13 @@ Welcome to pycmx's documentation!
.. toctree:: .. toctree::
:maxdepth: 5 :maxdepth: 5
:caption: API Reference: :caption: API Reference
pycmx function
classes
Indices and tables Indices and tables
================== ==================
* :ref:`genindex` * :ref:`genindex`
* :ref:`modindex`
* :ref:`search` * :ref:`search`
-7
View File
@@ -1,7 +0,0 @@
pycmx
=====
.. toctree::
:maxdepth: 4
pycmx
-46
View File
@@ -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:
+2 -3
View File
@@ -3,13 +3,12 @@
pycmx is a module for parsing CMX 3600-style EDLs. For more information and pycmx is a module for parsing CMX 3600-style EDLs. For more information and
examples see README.md examples see README.md
This module (c) 2018 Jamie Hardt. For more information on your rights to This module (c) 2022 Jamie Hardt. For more information on your rights to
copy and reuse this software, refer to the LICENSE file included with the copy and reuse this software, refer to the LICENSE file included with the
distribution. distribution.
""" """
__version__ = '0.8' __version__ = '1.2.0'
__author__ = 'Jamie Hardt'
from .parse_cmx_events import parse_cmx3600 from .parse_cmx_events import parse_cmx3600
from .transition import Transition from .transition import Transition
+25 -10
View File
@@ -2,13 +2,14 @@
# (c) 2018 Jamie Hardt # (c) 2018 Jamie Hardt
from re import (compile, match) from re import (compile, match)
from typing import Dict, Tuple
class ChannelMap: class ChannelMap:
""" """
Represents a set of all the channels to which an event applies. Represents a set of all the channels to which an event applies.
""" """
_chan_map = { _chan_map : Dict[str, Tuple] = {
"V" : (True, False, False), "V" : (True, False, False),
"A" : (False, True, False), "A" : (False, True, False),
"A2" : (False, False, True), "A2" : (False, False, True),
@@ -27,6 +28,11 @@ class ChannelMap:
'True if video is included' 'True if video is included'
return self.v return self.v
@property
def audio(self):
'True if an audio channel is included'
return len(self._audio_channel_set) > 0
@property @property
def channels(self): def channels(self):
'A generator for each audio channel' 'A generator for each audio channel'
@@ -35,7 +41,7 @@ class ChannelMap:
@property @property
def a1(self): def a1(self):
"""True if A1 is included.""" """True if A1 is included"""
return self.get_audio_channel(1) return self.get_audio_channel(1)
@a1.setter @a1.setter
@@ -44,7 +50,7 @@ class ChannelMap:
@property @property
def a2(self): def a2(self):
"""True if A2 is included.""" """True if A2 is included"""
return self.get_audio_channel(2) return self.get_audio_channel(2)
@a2.setter @a2.setter
@@ -53,7 +59,7 @@ class ChannelMap:
@property @property
def a3(self): def a3(self):
"""True if A3 is included.""" """True if A3 is included"""
return self.get_audio_channel(3) return self.get_audio_channel(3)
@a3.setter @a3.setter
@@ -62,7 +68,7 @@ class ChannelMap:
@property @property
def a4(self): def a4(self):
"""True if A4 is included.""" """True if A4 is included"""
return self.get_audio_channel(4) return self.get_audio_channel(4)
@a4.setter @a4.setter
@@ -70,18 +76,18 @@ class ChannelMap:
self.set_audio_channel(4,val) self.set_audio_channel(4,val)
def get_audio_channel(self,chan_num): 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) return (chan_num in self._audio_channel_set)
def set_audio_channel(self,chan_num,enabled): 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: if enabled:
self._audio_channel_set.add(chan_num) self._audio_channel_set.add(chan_num)
elif self.get_audio_channel(chan_num): elif self.get_audio_channel(chan_num):
self._audio_channel_set.remove(chan_num) self._audio_channel_set.remove(chan_num)
def _append_event(self, event_str): 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: if event_str in self._chan_map:
channels = self._chan_map[event_str] channels = self._chan_map[event_str]
self.v = channels[0] self.v = channels[0]
@@ -93,6 +99,15 @@ class ChannelMap:
self.set_audio_channel(int( matchresult.group(1)), True ) self.set_audio_channel(int( matchresult.group(1)), True )
def _append_ext(self, audio_ext): def _append_ext(self, audio_ext):
self.a3 = ext.audio3 self.a3 = audio_ext.audio3
self.a4 = ext.audio4 self.a4 = audio_ext.audio4
def __or__(self, other):
"""
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)
+52 -4
View File
@@ -1,8 +1,9 @@
# pycmx # pycmx
# (c) 2018 Jamie Hardt # (c) 2018 Jamie Hardt
from .parse_cmx_statements import (StmtUnrecognized, StmtFCM, StmtEvent) from .parse_cmx_statements import (StmtUnrecognized, StmtFCM, StmtEvent, StmtSourceUMID)
from .event import Event from .event import Event
from .channel_map import ChannelMap
class EditList: class EditList:
""" """
@@ -13,13 +14,47 @@ class EditList:
self.title_statement = statements[0] self.title_statement = statements[0]
self.event_statements = statements[1:] self.event_statements = statements[1:]
@property
def format(self):
"""
The detected format of the EDL. Possible values are: `3600`,`File32`,
`File128`, and `unknown`
"""
first_event = next( (s for s in self.event_statements if type(s) is StmtEvent), None)
if first_event:
if first_event.format == 8:
return '3600'
elif first_event.format == 32:
return 'File32'
elif first_event.format == 128:
return 'File128'
else:
return 'unknown'
else:
return 'unknown'
@property
def channels(self):
"""
Return the union of every channel channel.
"""
retval = ChannelMap()
for event in self.events:
for edit in event.edits:
retval = retval | edit.channels
return retval
@property @property
def title(self): def title(self):
""" """
The title of this edit list, as attensted by the 'TITLE:' statement on The title of this edit list.
the first line.
""" """
'The title of the edit list'
return self.title_statement.title return self.title_statement.title
@@ -54,8 +89,21 @@ class EditList:
else: else:
event_statements.append(stmt) event_statements.append(stmt)
elif type(stmt) is StmtSourceUMID:
break
else: else:
event_statements.append(stmt) event_statements.append(stmt)
yield Event(statements=event_statements) yield Event(statements=event_statements)
@property
def sources(self):
"""
A generator for all of the sources in the list
"""
for stmt in self.event_statements:
if type(stmt) is StmtSourceUMID:
yield stmt
+1 -1
View File
@@ -14,7 +14,7 @@ def parse_cmx3600(f):
f : a file-like object, anything that's readlines-able. f : a file-like object, anything that's readlines-able.
Returns: Returns:
An :class:`EditList`. An :class:`pycmx.edit_list.EditList`.
""" """
statements = parse_cmx3600_statements(f) statements = parse_cmx3600_statements(f)
return EditList(statements) return EditList(statements)
+8 -7
View File
@@ -11,13 +11,13 @@ from .util import collimate
StmtTitle = namedtuple("Title",["title","line_number"]) StmtTitle = namedtuple("Title",["title","line_number"])
StmtFCM = namedtuple("FCM",["drop","line_number"]) StmtFCM = namedtuple("FCM",["drop","line_number"])
StmtEvent = namedtuple("Event",["event","source","channels","trans",\ StmtEvent = namedtuple("Event",["event","source","channels","trans",\
"trans_op","source_in","source_out","record_in","record_out","line_number"]) "trans_op","source_in","source_out","record_in","record_out","format","line_number"])
StmtAudioExt = namedtuple("AudioExt",["audio3","audio4","line_number"]) StmtAudioExt = namedtuple("AudioExt",["audio3","audio4","line_number"])
StmtClipName = namedtuple("ClipName",["name","affect","line_number"]) StmtClipName = namedtuple("ClipName",["name","affect","line_number"])
StmtSourceFile = namedtuple("SourceFile",["filename","line_number"]) StmtSourceFile = namedtuple("SourceFile",["filename","line_number"])
StmtRemark = namedtuple("Remark",["text","line_number"]) StmtRemark = namedtuple("Remark",["text","line_number"])
StmtEffectsName = namedtuple("EffectsName",["name","line_number"]) StmtEffectsName = namedtuple("EffectsName",["name","line_number"])
StmtTrailer = namedtuple("Trailer",["text","line_number"]) StmtSourceUMID = namedtuple("Source",["name","umid","line_number"])
StmtSplitEdit = namedtuple("SplitEdit",["video","magnitue", "line_number"]) StmtSplitEdit = namedtuple("SplitEdit",["video","magnitue", "line_number"])
StmtMotionMemory = namedtuple("MotionMemory",["source","fps"]) # FIXME needs more fields StmtMotionMemory = namedtuple("MotionMemory",["source","fps"]) # FIXME needs more fields
StmtUnrecognized = namedtuple("Unrecognized",["content","line_number"]) StmtUnrecognized = namedtuple("Unrecognized",["content","line_number"])
@@ -69,8 +69,8 @@ def _parse_cmx3600_line(line, line_number):
return _parse_extended_audio_channels(line,line_number) return _parse_extended_audio_channels(line,line_number)
elif line.startswith("*"): elif line.startswith("*"):
return _parse_remark( line[1:].strip(), line_number) return _parse_remark( line[1:].strip(), line_number)
elif line.startswith(">>>"): elif line.startswith(">>> SOURCE"):
return _parse_trailer_statement(line, line_number) return _parse_source_umid_statement(line, line_number)
elif line.startswith("EFFECTS NAME IS"): elif line.startswith("EFFECTS NAME IS"):
return _parse_effects_name(line, line_number) return _parse_effects_name(line, line_number)
elif line.startswith("SPLIT:"): elif line.startswith("SPLIT:"):
@@ -157,10 +157,11 @@ def _parse_columns_for_standard_form(line, event_field_length, source_field_leng
source_out=column_strings[12].strip(), source_out=column_strings[12].strip(),
record_in=column_strings[14].strip(), record_in=column_strings[14].strip(),
record_out=column_strings[16].strip(), record_out=column_strings[16].strip(),
line_number=line_number) line_number=line_number,
format=source_field_length)
def _parse_trailer_statement(line, line_number): def _parse_source_umid_statement(line, line_number):
trimmed = line[3:].strip() trimmed = line[3:].strip()
return StmtTrailer(trimmed, line_number=line_number) return StmtSourceUMID(name=None, umid=None, line_number=line_number)
+3 -3
View File
@@ -70,12 +70,12 @@ class Transition:
@property @property
def key_background(self): def key_background(self):
"`True` if this edit is a key background." "`True` if this edit is a key background."
return self.transition == KeyBackground return self.transition == Transition.KeyBackground
@property @property
def key_foreground(self): def key_foreground(self):
"`True` if this edit is a key foreground." "`True` if this edit is a key foreground."
return self.transition == Key return self.transition == Transition.Key
@property @property
def key_out(self): def key_out(self):
@@ -83,4 +83,4 @@ class Transition:
`True` if this edit is a key out. This material will removed from `True` if this edit is a key out. This material will removed from
the key foreground and replaced with the key background. the key foreground and replaced with the key background.
""" """
return self.transition == KeyOut return self.transition == Transition.KeyOut
+60
View File
@@ -0,0 +1,60 @@
[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
[project]
name = "pycmx"
authors = [{name = "Jamie Hardt", email = "jamiehardt@me.com"}]
readme = "README.md"
dynamic = ["version", "description"]
requires-python = "~=3.7"
classifiers = [
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: MIT License',
'Topic :: Multimedia',
'Topic :: Multimedia :: Video',
'Topic :: Text Processing',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11'
]
dependencies = [
]
keywords = [
'parser',
'film',
'broadcast'
]
[tool.flit.module]
name = "pycmx"
[project.optional-dependencies]
doc = [
'sphinx >= 5.3.0',
'sphinx_rtd_theme >= 1.1.1',
]
[project.urls]
Home = "https://github.com/iluvcapra/pycmx"
Documentation = "https://pycmx.readthedocs.io/"
Source = "https://github.com/iluvcapra/pycmx.git"
Issues = "https://github.com/iluvcapra/pycmx/issues"
[tool.pyright]
typeCheckingMode = "basic"
[tool.pylint]
max-line-length = 88
disable = [
"C0103", # (invalid-name)
"C0114", # (missing-module-docstring)
"C0115", # (missing-class-docstring)
"C0116", # (missing-function-docstring)
"R0903", # (too-few-public-methods)
"R0913", # (too-many-arguments)
"W0105", # (pointless-string-statement)
]
-19
View File
@@ -1,19 +0,0 @@
from setuptools import setup
with open("README.md", "r") as fh:
long_description = fh.read()
setup(name='pycmx',
version='0.8',
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,
url='https://github.com/iluvcapra/pycmx',
classifiers=['Development Status :: 4 - Beta',
'License :: OSI Approved :: MIT License',
'Topic :: Multimedia',
'Topic :: Multimedia :: Video',
'Topic :: Text Processing'],
packages=['pycmx'])
+1
View File
@@ -0,0 +1 @@
from . import test_parse
+11 -4
View File
@@ -18,14 +18,15 @@ class TestParse(TestCase):
counts = [ 287, 466, 250 , 376, 120 , 3 , 466 ] counts = [ 287, 466, 250 , 376, 120 , 3 , 466 ]
for fn, count in zip(type(self).files, counts): for fn, count in zip(type(self).files, counts):
with open(f"tests/edls/{fn}" ,'r') as f: with open("tests/edls/" + fn ,'r') as f:
edl = pycmx.parse_cmx3600(f) edl = pycmx.parse_cmx3600(f)
actual = len( list( edl.events )) actual = len( list( edl.events ))
self.assertTrue( actual == count , f"expected {count} in file {fn} but found {actual}") self.assertTrue( actual == count ,
"expected %i in file %s but found %i" % (count, fn, actual))
def test_list_sanity(self): def test_list_sanity(self):
for fn in type(self).files: for fn in type(self).files:
with open(f"tests/edls/{fn}" ,'r') as f: with open("tests/edls/" + fn ,'r') as f:
edl = pycmx.parse_cmx3600(f) edl = pycmx.parse_cmx3600(f)
self.assertTrue( type(edl.title) is str ) self.assertTrue( type(edl.title) is str )
self.assertTrue( len(edl.title) > 0 ) self.assertTrue( len(edl.title) > 0 )
@@ -33,7 +34,8 @@ class TestParse(TestCase):
def test_event_sanity(self): def test_event_sanity(self):
for fn in type(self).files: for fn in type(self).files:
with open(f"tests/edls/{fn}" ,'r') as f: path = "tests/edls/" + fn
with open(path ,'r') as f:
edl = pycmx.parse_cmx3600(f) edl = pycmx.parse_cmx3600(f)
for index, event in enumerate(edl.events): for index, event in enumerate(edl.events):
self.assertTrue( len(event.edits) > 0 ) self.assertTrue( len(event.edits) > 0 )
@@ -64,6 +66,7 @@ class TestParse(TestCase):
self.assertFalse( events[0].edits[0].channels.a1) self.assertFalse( events[0].edits[0].channels.a1)
self.assertTrue( events[0].edits[0].channels.a2) 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.get_audio_channel(7) )
self.assertTrue( events[2].edits[0].channels.audio)
def test_multi_edit_events(self): def test_multi_edit_events(self):
@@ -104,3 +107,7 @@ class TestParse(TestCase):
edl = pycmx.parse_cmx3600(f) edl = pycmx.parse_cmx3600(f)
events = list(edl.events) events = list(edl.events)
self.assertEqual( events[4].edits[1].transition.name , "CROSS DISSOLVE" ) self.assertEqual( events[4].edits[1].transition.name , "CROSS DISSOLVE" )
# add test for edit_list.channels