mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 17:00:53 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd35f4f80d | ||
|
|
6525840151 | ||
|
|
b78ae05d8c | ||
|
|
51ed92f5df | ||
|
|
a6f042c76f | ||
|
|
179808fbf2 | ||
|
|
2b38d8aaf9 | ||
|
|
e19bb3c5c5 | ||
|
|
d0e2e5bbe5 | ||
|
|
e90897e503 | ||
|
|
4daa008e91 | ||
|
|
cf1d71800e | ||
|
|
fcb25ae183 | ||
|
|
97ada84bfe | ||
|
|
d157512c32 | ||
|
|
1e2d31716e | ||
|
|
6be7c54de5 |
26
.github/workflows/pythonpublish.yml
vendored
26
.github/workflows/pythonpublish.yml
vendored
@@ -2,28 +2,32 @@ name: Upload Python Package
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3.5.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v4.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install setuptools wheel twine
|
pip install build
|
||||||
- name: Build and publish
|
- name: Build package
|
||||||
env:
|
run: python -m build
|
||||||
TWINE_USERNAME: __token__
|
- name: pypi-publish
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_APIKEY }}
|
uses: pypa/gh-action-pypi-publish@v1.8.6
|
||||||
run: |
|
|
||||||
python setup.py sdist bdist_wheel
|
|
||||||
twine upload dist/*
|
|
||||||
- name: Report to Mastodon
|
- name: Report to Mastodon
|
||||||
uses: cbrgm/mastodon-github-action@v1.0.1
|
uses: cbrgm/mastodon-github-action@v1.0.1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -15,18 +15,19 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
print(sys.path)
|
|
||||||
|
import pycmx
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = u'pycmx'
|
project = u'pycmx'
|
||||||
copyright = u'2022, Jamie Hardt'
|
copyright = u'(c) 2023, Jamie Hardt'
|
||||||
author = u'Jamie Hardt'
|
author = u'Jamie Hardt'
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = u''
|
version = pycmx.__version__
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = u''
|
release = pycmx.__version__
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
pycmx is a module for parsing CMX 3600-style EDLs. For more information and
|
pycmx is a parser for CMX 3600-style EDLs.
|
||||||
examples see README.md
|
|
||||||
|
|
||||||
This module (c) 2022 Jamie Hardt. For more information on your rights to
|
This module (c) 2023 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__ = '1.2.0'
|
__version__ = '1.2.1'
|
||||||
|
|
||||||
from .parse_cmx_events import parse_cmx3600
|
from .parse_cmx_events import parse_cmx3600
|
||||||
from .transition import Transition
|
from .transition import Transition
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# (c) 2018 Jamie Hardt
|
# (c) 2018 Jamie Hardt
|
||||||
|
|
||||||
from re import (compile, match)
|
from re import (compile, match)
|
||||||
from typing import Dict, Tuple
|
from typing import Dict, Tuple, Generator
|
||||||
|
|
||||||
class ChannelMap:
|
class ChannelMap:
|
||||||
"""
|
"""
|
||||||
@@ -24,62 +24,62 @@ class ChannelMap:
|
|||||||
self.v = v
|
self.v = v
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def video(self):
|
def video(self) -> bool:
|
||||||
'True if video is included'
|
'True if video is included'
|
||||||
return self.v
|
return self.v
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def audio(self):
|
def audio(self) -> bool:
|
||||||
'True if an audio channel is included'
|
'True if an audio channel is included'
|
||||||
return len(self._audio_channel_set) > 0
|
return len(self._audio_channel_set) > 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self):
|
def channels(self) -> Generator[int, None, None]:
|
||||||
'A generator for each audio channel'
|
'A generator for each audio channel'
|
||||||
for c in self._audio_channel_set:
|
for c in self._audio_channel_set:
|
||||||
yield c
|
yield c
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def a1(self):
|
def a1(self) -> bool:
|
||||||
"""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
|
||||||
def a1(self,val):
|
def a1(self, val: bool):
|
||||||
self.set_audio_channel(1,val)
|
self.set_audio_channel(1,val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def a2(self):
|
def a2(self) -> bool:
|
||||||
"""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
|
||||||
def a2(self,val):
|
def a2(self, val: bool):
|
||||||
self.set_audio_channel(2,val)
|
self.set_audio_channel(2,val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def a3(self):
|
def a3(self) -> bool:
|
||||||
"""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
|
||||||
def a3(self,val):
|
def a3(self, val: bool):
|
||||||
self.set_audio_channel(3,val)
|
self.set_audio_channel(3,val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def a4(self):
|
def a4(self) -> bool:
|
||||||
"""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
|
||||||
def a4(self,val):
|
def a4(self,val: bool):
|
||||||
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) -> bool:
|
||||||
"""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: bool):
|
||||||
"""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)
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
from .transition import Transition
|
from .transition import Transition
|
||||||
from .channel_map import ChannelMap
|
from .channel_map import ChannelMap
|
||||||
from .parse_cmx_statements import StmtEffectsName
|
# from .parse_cmx_statements import StmtEffectsName
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class Edit:
|
class Edit:
|
||||||
"""
|
"""
|
||||||
@@ -18,7 +20,7 @@ class Edit:
|
|||||||
self.trans_name_statement = trans_name_statement
|
self.trans_name_statement = trans_name_statement
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def line_number(self):
|
def line_number(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the line number for the "standard form" statement associated with
|
Get the line number for the "standard form" statement associated with
|
||||||
this edit. Line numbers a zero-indexed, such that the
|
this edit. Line numbers a zero-indexed, such that the
|
||||||
@@ -27,7 +29,7 @@ class Edit:
|
|||||||
return self.edit_statement.line_number
|
return self.edit_statement.line_number
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self):
|
def channels(self) -> ChannelMap:
|
||||||
"""
|
"""
|
||||||
Get the :obj:`ChannelMap` object associated with this Edit.
|
Get the :obj:`ChannelMap` object associated with this Edit.
|
||||||
"""
|
"""
|
||||||
@@ -38,7 +40,7 @@ class Edit:
|
|||||||
return cm
|
return cm
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def transition(self):
|
def transition(self) -> Transition:
|
||||||
"""
|
"""
|
||||||
Get the :obj:`Transition` object associated with this edit.
|
Get the :obj:`Transition` object associated with this edit.
|
||||||
"""
|
"""
|
||||||
@@ -48,14 +50,14 @@ class Edit:
|
|||||||
return Transition(self.edit_statement.trans, self.edit_statement.trans_op, None)
|
return Transition(self.edit_statement.trans, self.edit_statement.trans_op, None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_in(self):
|
def source_in(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the source in timecode.
|
Get the source in timecode.
|
||||||
"""
|
"""
|
||||||
return self.edit_statement.source_in
|
return self.edit_statement.source_in
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_out(self):
|
def source_out(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the source out timecode.
|
Get the source out timecode.
|
||||||
"""
|
"""
|
||||||
@@ -63,7 +65,7 @@ class Edit:
|
|||||||
return self.edit_statement.source_out
|
return self.edit_statement.source_out
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def record_in(self):
|
def record_in(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the record in timecode.
|
Get the record in timecode.
|
||||||
"""
|
"""
|
||||||
@@ -71,7 +73,7 @@ class Edit:
|
|||||||
return self.edit_statement.record_in
|
return self.edit_statement.record_in
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def record_out(self):
|
def record_out(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the record out timecode.
|
Get the record out timecode.
|
||||||
"""
|
"""
|
||||||
@@ -79,7 +81,7 @@ class Edit:
|
|||||||
return self.edit_statement.record_out
|
return self.edit_statement.record_out
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self):
|
def source(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the source column. This is the 8, 32 or 128-character string on the
|
Get the source column. This is the 8, 32 or 128-character string on the
|
||||||
event record line, this usually references the tape name of the source.
|
event record line, this usually references the tape name of the source.
|
||||||
@@ -87,21 +89,21 @@ class Edit:
|
|||||||
return self.edit_statement.source
|
return self.edit_statement.source
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def black(self):
|
def black(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Black video or silence should be used as the source for this event.
|
Black video or silence should be used as the source for this event.
|
||||||
"""
|
"""
|
||||||
return self.source == "BL"
|
return self.source == "BL"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def aux_source(self):
|
def aux_source(self) -> bool:
|
||||||
"""
|
"""
|
||||||
An auxiliary source is the source of this event.
|
An auxiliary source is the source of this event.
|
||||||
"""
|
"""
|
||||||
return self.source == "AX"
|
return self.source == "AX"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_file(self):
|
def source_file(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Get the source file, as attested by a "* SOURCE FILE" remark on the
|
Get the source file, as attested by a "* SOURCE FILE" remark on the
|
||||||
EDL. This will return None if the information is not present.
|
EDL. This will return None if the information is not present.
|
||||||
@@ -112,7 +114,7 @@ class Edit:
|
|||||||
return self.source_file_statement.filename
|
return self.source_file_statement.filename
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def clip_name(self):
|
def clip_name(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Get the clip name, as attested by a "* FROM CLIP NAME" or "* TO CLIP
|
Get the clip name, as attested by a "* FROM CLIP NAME" or "* TO CLIP
|
||||||
NAME" remark on the EDL. This will return None if the information is
|
NAME" remark on the EDL. This will return None if the information is
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ from .parse_cmx_statements import (StmtUnrecognized, StmtFCM, StmtEvent, StmtSou
|
|||||||
from .event import Event
|
from .event import Event
|
||||||
from .channel_map import ChannelMap
|
from .channel_map import ChannelMap
|
||||||
|
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
class EditList:
|
class EditList:
|
||||||
"""
|
"""
|
||||||
Represents an entire edit decision list as returned by `parse_cmx3600()`.
|
Represents an entire edit decision list as returned by :func:`~pycmx.parse_cmx3600()`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, statements):
|
def __init__(self, statements):
|
||||||
self.title_statement = statements[0]
|
self.title_statement = statements[0]
|
||||||
self.event_statements = statements[1:]
|
self.event_statements = statements[1:]
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def format(self):
|
def format(self) -> str:
|
||||||
"""
|
"""
|
||||||
The detected format of the EDL. Possible values are: `3600`,`File32`,
|
The detected format of the EDL. Possible values are: "3600", "File32",
|
||||||
`File128`, and `unknown`
|
"File128", and "unknown".
|
||||||
"""
|
"""
|
||||||
first_event = next( (s for s in self.event_statements if type(s) is StmtEvent), None)
|
first_event = next( (s for s in self.event_statements if type(s) is StmtEvent), None)
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class EditList:
|
|||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self):
|
def channels(self) -> ChannelMap:
|
||||||
"""
|
"""
|
||||||
Return the union of every channel channel.
|
Return the union of every channel channel.
|
||||||
"""
|
"""
|
||||||
@@ -51,7 +51,7 @@ class EditList:
|
|||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self) -> str:
|
||||||
"""
|
"""
|
||||||
The title of this edit list.
|
The title of this edit list.
|
||||||
"""
|
"""
|
||||||
@@ -59,7 +59,7 @@ class EditList:
|
|||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unrecognized_statements(self):
|
def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
|
||||||
"""
|
"""
|
||||||
A generator for all the unrecognized statements in the list.
|
A generator for all the unrecognized statements in the list.
|
||||||
"""
|
"""
|
||||||
@@ -69,7 +69,7 @@ class EditList:
|
|||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def events(self):
|
def events(self) -> Generator[Event, None, None]:
|
||||||
'A generator for all the events in the edit list'
|
'A generator for all the events in the edit list'
|
||||||
is_drop = None
|
is_drop = None
|
||||||
current_event_num = None
|
current_event_num = None
|
||||||
@@ -97,7 +97,7 @@ class EditList:
|
|||||||
yield Event(statements=event_statements)
|
yield Event(statements=event_statements)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sources(self):
|
def sources(self) -> Generator[StmtSourceUMID, None, None]:
|
||||||
"""
|
"""
|
||||||
A generator for all of the sources in the list
|
A generator for all of the sources in the list
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
# pycmx
|
# pycmx
|
||||||
# (c) 2018 Jamie Hardt
|
# (c) 2023 Jamie Hardt
|
||||||
|
|
||||||
from .parse_cmx_statements import (StmtEvent, StmtClipName, StmtSourceFile, StmtAudioExt, StmtUnrecognized, StmtEffectsName)
|
from .parse_cmx_statements import (StmtEvent, StmtClipName, StmtSourceFile, StmtAudioExt, StmtUnrecognized, StmtEffectsName)
|
||||||
from .edit import Edit
|
from .edit import Edit
|
||||||
|
|
||||||
|
from typing import List, Generator, Optional, Tuple, Any
|
||||||
|
|
||||||
class Event:
|
class Event:
|
||||||
"""
|
"""
|
||||||
Represents a collection of :class:`Edit`s, all with the same event number.
|
Represents a collection of :class:`~pycmx.edit.Edit` s, all with the same event number.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, statements):
|
def __init__(self, statements):
|
||||||
self.statements = statements
|
self.statements = statements
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number(self):
|
def number(self) -> int:
|
||||||
"""
|
"""
|
||||||
Return the event number.
|
Return the event number.
|
||||||
"""
|
"""
|
||||||
return int(self._edit_statements()[0].event)
|
return int(self._edit_statements()[0].event)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def edits(self):
|
def edits(self) -> List[Edit]:
|
||||||
"""
|
"""
|
||||||
Returns the edits. Most events will have a single edit, a single event
|
Returns the edits. Most events will have a single edit, a single event
|
||||||
will have multiple edits when a dissolve, wipe or key transition needs
|
will have multiple edits when a dissolve, wipe or key transition needs
|
||||||
@@ -30,17 +32,19 @@ class Event:
|
|||||||
clip_names = self._clip_name_statements()
|
clip_names = self._clip_name_statements()
|
||||||
source_files= self._source_file_statements()
|
source_files= self._source_file_statements()
|
||||||
|
|
||||||
the_zip = [edits_audio]
|
the_zip: List[List[Any]] = [edits_audio]
|
||||||
|
|
||||||
if len(edits_audio) == 2:
|
if len(edits_audio) == 2:
|
||||||
cn = [None, None]
|
start_name: Optional[StmtClipName] = None
|
||||||
|
end_name: Optional[StmtClipName] = None
|
||||||
|
|
||||||
for clip_name in clip_names:
|
for clip_name in clip_names:
|
||||||
if clip_name.affect == 'from':
|
if clip_name.affect == 'from':
|
||||||
cn[0] = clip_name
|
start_name = clip_name
|
||||||
elif clip_name.affect == 'to':
|
elif clip_name.affect == 'to':
|
||||||
cn[1] = clip_name
|
end_name = clip_name
|
||||||
|
|
||||||
the_zip.append(cn)
|
the_zip.append([start_name, end_name])
|
||||||
else:
|
else:
|
||||||
if len(edits_audio) == len(clip_names):
|
if len(edits_audio) == len(clip_names):
|
||||||
the_zip.append(clip_names)
|
the_zip.append(clip_names)
|
||||||
@@ -57,17 +61,20 @@ class Event:
|
|||||||
# attach trans name to last event
|
# attach trans name to last event
|
||||||
try:
|
try:
|
||||||
trans_statement = self._trans_name_statements()[0]
|
trans_statement = self._trans_name_statements()[0]
|
||||||
trans_names = [None] * (len(edits_audio) - 1)
|
trans_names: List[Optional[Any]] = [None] * (len(edits_audio) - 1)
|
||||||
trans_names.append(trans_statement)
|
trans_names.append(trans_statement)
|
||||||
the_zip.append(trans_names)
|
the_zip.append(trans_names)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
the_zip.append([None] * len(edits_audio) )
|
the_zip.append([None] * len(edits_audio) )
|
||||||
|
|
||||||
|
return [ Edit(edit_statement=e1[0],
|
||||||
return [ Edit(e1[0],e1[1],n1,s1,u1) for (e1,n1,s1,u1) in zip(*the_zip) ]
|
audio_ext_statement=e1[1],
|
||||||
|
clip_name_statement=n1,
|
||||||
|
source_file_statement=s1,
|
||||||
|
trans_name_statement=u1) for (e1,n1,s1,u1) in zip(*the_zip) ]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unrecognized_statements(self):
|
def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
|
||||||
"""
|
"""
|
||||||
A generator for all the unrecognized statements in the event.
|
A generator for all the unrecognized statements in the event.
|
||||||
"""
|
"""
|
||||||
@@ -75,19 +82,19 @@ class Event:
|
|||||||
if type(s) is StmtUnrecognized:
|
if type(s) is StmtUnrecognized:
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def _trans_name_statements(self):
|
def _trans_name_statements(self) -> List[StmtEffectsName]:
|
||||||
return [s for s in self.statements if type(s) is StmtEffectsName]
|
return [s for s in self.statements if type(s) is StmtEffectsName]
|
||||||
|
|
||||||
def _edit_statements(self):
|
def _edit_statements(self) -> List[StmtEvent]:
|
||||||
return [s for s in self.statements if type(s) is StmtEvent]
|
return [s for s in self.statements if type(s) is StmtEvent]
|
||||||
|
|
||||||
def _clip_name_statements(self):
|
def _clip_name_statements(self) -> List[StmtClipName]:
|
||||||
return [s for s in self.statements if type(s) is StmtClipName]
|
return [s for s in self.statements if type(s) is StmtClipName]
|
||||||
|
|
||||||
def _source_file_statements(self):
|
def _source_file_statements(self) -> List[StmtSourceFile]:
|
||||||
return [s for s in self.statements if type(s) is StmtSourceFile]
|
return [s for s in self.statements if type(s) is StmtSourceFile]
|
||||||
|
|
||||||
def _statements_with_audio_ext(self):
|
def _statements_with_audio_ext(self) -> Generator[Tuple[StmtEvent, Optional[StmtAudioExt]], None, None]:
|
||||||
for (s1, s2) in zip(self.statements, self.statements[1:]):
|
for (s1, s2) in zip(self.statements, self.statements[1:]):
|
||||||
if type(s1) is StmtEvent and type(s2) is StmtAudioExt:
|
if type(s1) is StmtEvent and type(s2) is StmtAudioExt:
|
||||||
yield (s1,s2)
|
yield (s1,s2)
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
# pycmx
|
# pycmx
|
||||||
# (c) 2018 Jamie Hardt
|
# (c) 2018 Jamie Hardt
|
||||||
|
|
||||||
from collections import namedtuple
|
# from collections import namedtuple
|
||||||
|
|
||||||
from .parse_cmx_statements import (parse_cmx3600_statements, StmtEvent,StmtFCM )
|
from .parse_cmx_statements import (parse_cmx3600_statements)
|
||||||
from .edit_list import EditList
|
from .edit_list import EditList
|
||||||
|
|
||||||
def parse_cmx3600(f):
|
from typing import TextIO
|
||||||
|
|
||||||
|
def parse_cmx3600(f: TextIO):
|
||||||
"""
|
"""
|
||||||
Parse a CMX 3600 EDL.
|
Parse a CMX 3600 EDL.
|
||||||
|
|
||||||
Args:
|
:param TextIO f: a file-like object, anything that's readlines-able.
|
||||||
f : a file-like object, anything that's readlines-able.
|
:returns: An :class:`pycmx.edit_list.EditList`.
|
||||||
|
|
||||||
Returns:
|
|
||||||
An :class:`pycmx.edit_list.EditList`.
|
|
||||||
"""
|
"""
|
||||||
statements = parse_cmx3600_statements(f)
|
statements = parse_cmx3600_statements(f)
|
||||||
return EditList(statements)
|
return EditList(statements)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
from typing import TextIO, List
|
||||||
|
|
||||||
|
|
||||||
from .util import collimate
|
from .util import collimate
|
||||||
|
|
||||||
@@ -18,12 +20,12 @@ 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"])
|
||||||
StmtSourceUMID = namedtuple("Source",["name","umid","line_number"])
|
StmtSourceUMID = namedtuple("Source",["name","umid","line_number"])
|
||||||
StmtSplitEdit = namedtuple("SplitEdit",["video","magnitue", "line_number"])
|
StmtSplitEdit = namedtuple("SplitEdit",["video","magnitude", "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"])
|
||||||
|
|
||||||
|
|
||||||
def parse_cmx3600_statements(file):
|
def parse_cmx3600_statements(file: TextIO) -> List[object]:
|
||||||
"""
|
"""
|
||||||
Return a list of every statement in the file argument.
|
Return a list of every statement in the file argument.
|
||||||
"""
|
"""
|
||||||
@@ -109,7 +111,7 @@ def _parse_extended_audio_channels(line, line_number):
|
|||||||
else:
|
else:
|
||||||
return StmtUnrecognized(content=line, line_number=line_number)
|
return StmtUnrecognized(content=line, line_number=line_number)
|
||||||
|
|
||||||
def _parse_remark(line, line_number):
|
def _parse_remark(line, line_number) -> object:
|
||||||
if line.startswith("FROM CLIP NAME:"):
|
if line.startswith("FROM CLIP NAME:"):
|
||||||
return StmtClipName(name=line[15:].strip() , affect="from", line_number=line_number)
|
return StmtClipName(name=line[15:].strip() , affect="from", line_number=line_number)
|
||||||
elif line.startswith("TO CLIP NAME:"):
|
elif line.startswith("TO CLIP NAME:"):
|
||||||
@@ -119,7 +121,7 @@ def _parse_remark(line, line_number):
|
|||||||
else:
|
else:
|
||||||
return StmtRemark(text=line, line_number=line_number)
|
return StmtRemark(text=line, line_number=line_number)
|
||||||
|
|
||||||
def _parse_effects_name(line, line_number):
|
def _parse_effects_name(line, line_number) -> StmtEffectsName:
|
||||||
name = line[16:].strip()
|
name = line[16:].strip()
|
||||||
return StmtEffectsName(name=name, line_number=line_number)
|
return StmtEffectsName(name=name, line_number=line_number)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# pycmx
|
# pycmx
|
||||||
# (c) 2018 Jamie Hardt
|
# (c) 2023 Jamie Hardt
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class Transition:
|
class Transition:
|
||||||
"""
|
"""
|
||||||
@@ -19,7 +21,7 @@ class Transition:
|
|||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kind(self):
|
def kind(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Return the kind of transition: Cut, Wipe, etc
|
Return the kind of transition: Cut, Wipe, etc
|
||||||
"""
|
"""
|
||||||
@@ -37,22 +39,22 @@ class Transition:
|
|||||||
return Transition.KeyOut
|
return Transition.KeyOut
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cut(self):
|
def cut(self) -> bool:
|
||||||
"`True` if this transition is a cut."
|
"`True` if this transition is a cut."
|
||||||
return self.transition == 'C'
|
return self.transition == 'C'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dissolve(self):
|
def dissolve(self) -> bool:
|
||||||
"`True` if this traansition is a dissolve."
|
"`True` if this traansition is a dissolve."
|
||||||
return self.transition == 'D'
|
return self.transition == 'D'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wipe(self):
|
def wipe(self) -> bool:
|
||||||
"`True` if this transition is a wipe."
|
"`True` if this transition is a wipe."
|
||||||
return self.transition.startswith('W')
|
return self.transition.startswith('W')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect_duration(self):
|
def effect_duration(self) -> int:
|
||||||
"""The duration of this transition, in frames of the record target.
|
"""The duration of this transition, in frames of the record target.
|
||||||
|
|
||||||
In the event of a key event, this is the duration of the fade in.
|
In the event of a key event, this is the duration of the fade in.
|
||||||
@@ -60,7 +62,7 @@ class Transition:
|
|||||||
return int(self.operand)
|
return int(self.operand)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wipe_number(self):
|
def wipe_number(self) -> Optional[int]:
|
||||||
"Wipes are identified by a particular number."
|
"Wipes are identified by a particular number."
|
||||||
if self.wipe:
|
if self.wipe:
|
||||||
return int(self.transition[1:])
|
return int(self.transition[1:])
|
||||||
@@ -68,19 +70,21 @@ class Transition:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key_background(self):
|
def key_background(self) -> bool:
|
||||||
"`True` if this edit is a key background."
|
"`True` if this edit is a key background."
|
||||||
return self.transition == Transition.KeyBackground
|
return self.transition == Transition.KeyBackground
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key_foreground(self):
|
def key_foreground(self) -> bool:
|
||||||
"`True` if this edit is a key foreground."
|
"`True` if this edit is a key foreground."
|
||||||
return self.transition == Transition.Key
|
return self.transition == Transition.Key
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key_out(self):
|
def key_out(self) -> bool:
|
||||||
"""
|
"""
|
||||||
`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 == Transition.KeyOut
|
return self.transition == Transition.KeyOut
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user