diff --git a/.gitignore b/.gitignore index d2c1622ea..34795c2e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.swp *.pyc build* +dist* +*.egg-info .coverage diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..f9bb1080e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,2 @@ +Thank you for your interest in contributing to OpenTimelineIO! +We ask that potential contributors review our guidelines on [contributing](https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/Contributing) before submitting a pull request. diff --git a/EULA.pdf b/EULA.pdf deleted file mode 100644 index 744bc6c9f..000000000 Binary files a/EULA.pdf and /dev/null differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..d621dab16 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,175 @@ + + Modified Apache 2.0 License + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor + and its affiliates, except as required to comply with Section 4(c) of + the License and to reproduce the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/Makefile b/Makefile index 24fdf25bf..685d47569 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ -.PHONY: coverage test test_first_fail clean autopep8 lint doc-html +.PHONY: coverage test test_first_fail clean autopep8 lint doc-html \ + python-version COV_PROG := $(shell command -v coverage 2> /dev/null) PEP8_PROG := $(shell command -v pep8 2> /dev/null) -AUTOPEP8_PROG := $(shell command -v autopep8 2> /dev/null) PYFLAKES_PROG := $(shell command -v pyflakes 2> /dev/null) FLAKE8_PROG := $(shell command -v flake8 2> /dev/null) +# AUTOPEP8_PROG := $(shell command -v autopep8 2> /dev/null) TEST_ARGS= ifeq ($(VERBOSE), 1) @@ -14,30 +15,30 @@ endif # Clear the environment of a preset media linker OTIO_DEFAULT_MEDIA_LINKER = +python-version: + @python --version -test-core: +test-core: python-version @echo "Running Core tests..." - @python --version @python -m unittest discover tests $(TEST_ARGS) -test-contrib: +test-contrib: python-version @echo "Running Contrib tests..." - @python --version @make -C contrib/adapters test VERBOSE=$(VERBOSE) # run all the unit tests test: test-core test-contrib -coverage: +coverage: python-version ifndef COV_PROG $(error "coverage is not available please see: "\ "https://coverage.readthedocs.io/en/coverage-4.2/install.html") endif - @coverage run --source=opentimelineio -m unittest discover tests - @coverage report -m + @${COV_PROG} run --source=opentimelineio -m unittest discover tests + @${COV_PROG} report -m # run all the unit tests, stopping at the first failure -test_first_fail: +test_first_fail: python-version @python -m unittest discover tests --failfast # remove pyc files @@ -45,28 +46,28 @@ clean: rm */*.pyc */*/*.pyc # conform all files to pep8 -- WILL CHANGE FILES IN PLACE -autopep8: -ifndef AUTOPEP8_PROG - $(error "autopep8 is not available please see: "\ - "https://pypi.python.org/pypi/autopep8#installation") -endif - find . -name "*.py" | xargs autopep8 --aggressive --in-place -r +# autopep8: +# ifndef AUTOPEP8_PROG +# $(error "autopep8 is not available please see: "\ +# "https://pypi.python.org/pypi/autopep8#installation") +# endif +# find . -name "*.py" | xargs ${AUTOPEP8_PROG} --aggressive --in-place -r -# run the codebase through a linter +# run the codebase through flake8. pep8 and pyflakes are called by flake8. lint: ifndef PEP8_PROG - $(error "pep8 is not available please see: "\ + $(error "pep8 is not available on $$PATH please see: "\ "https://pypi.python.org/pypi/pep8#installation") endif ifndef PYFLAKES_PROG - $(error "pyflakes is not available please see: "\ + $(error "pyflakes is not available on $$PATH please see: "\ "https://pypi.python.org/pypi/pyflakes#installation") endif ifndef FLAKE8_PROG - $(error "flake8 is not available please see: "\ + $(error "flake8 is not available on $$PATH please see: "\ "http://flake8.pycqa.org/en/latest/index.html#installation") endif - @flake8 opentimelineio bin examples tests + @python -m flake8 --exclude build # generate documentation in html doc-html: diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 000000000..11a3178b8 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,10 @@ +OpenTimelineIO +Copyright 2017 Pixar + +All rights reserved. + + + +This product includes software developed at: + +Pixar (http://www.pixar.com/). \ No newline at end of file diff --git a/OTIO_CLA_Corporate.pdf b/OTIO_CLA_Corporate.pdf new file mode 100644 index 000000000..e2f19ad4b Binary files /dev/null and b/OTIO_CLA_Corporate.pdf differ diff --git a/OTIO_CLA_Individual.pdf b/OTIO_CLA_Individual.pdf new file mode 100644 index 000000000..174107259 Binary files /dev/null and b/OTIO_CLA_Individual.pdf differ diff --git a/README.md b/README.md index 41a85784a..ae2143c7f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,17 @@ OpenTimelineIO ============== +http://opentimeline.io/ + ![Supported Versions](https://img.shields.io/badge/python-2.7%2C%203.5-blue.svg) -PRE-RELEASE NOTICE ------------------ +PUBLIC BETA NOTICE +------------------ -We intend to release OpenTimelineIO as an open source project. Prior to -release, a few people (like you) have early access to the project. Please see -the contact section at the bottom if you have questions about this. +OpenTimelineIO is currently in Public Beta. That means that it may be missing +some essential features and there are large changes planned. During this phase +we actively encourage you to provide feedback, requests, comments, and/or +contributions. Overview -------- @@ -17,9 +20,9 @@ OpenTimelineIO is an interchange format and API for editorial cut information. OTIO is not a container format for media, rather it contains information about the order and length of cuts and references to external media. -OTIO includes both a file format and an API for manipulating that format. It +OTIO includes both a file format and an API for manipulating that format. It also includes a plugin architecture for writing adapters to convert -from/to existing editorial timeline formats. It also implements a dependency- +from/to existing editorial timeline formats. It also implements a dependency- less library for dealing strictly with time, `opentime`. You can provide adapters for your video editing tool or pipeline as needed. @@ -44,6 +47,8 @@ Use Cases - Editorial is working with proxy media (QuickTime, MXF, etc.) and I want to gather all the EXRs that correspond with that & bring those into Nuke. +For more use cases, see: https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/Use-Cases + Architecture ------------ @@ -59,7 +64,7 @@ formats: Final Cut 7 XML Format - Status: Supported via the `fcp_xml` adapter -- https://developer.apple.com/library/content/documentation/AppleApplications/Reference/FinalCutPro_XML/AboutThisDoc/AboutThisDoc.html#//apple_ref/doc/uid/TP30001152-TPXREF101 +- https://developer.apple.com/library/content/documentation/AppleApplications/Reference/FinalCutPro_XML/AboutThisDoc/AboutThisDoc.html#//apple_ref/doc/uid/TP30001152-TPXREF101 Final Cut Pro X XML Format: - Status: https://github.com/PixarAnimationStudios/OpenTimelineIO/issues/37 @@ -67,11 +72,13 @@ Final Cut Pro X XML Format: ### Adobe Premiere Project ### -- Based on conversations with Adobe, we support interchange with Adobe Premiere via the FCP 7 XML format (see above). +- Based on guidance from Adobe, we support interchange with Adobe Premiere via + the FCP 7 XML format (see above). ### CMX3600 EDL ### - Status: Supported via the `cmx_3600` adapter +- Full specification: SMPTE 258M-2004 "For Television −− Transfer of Edit Decision Lists" - http://xmil.biz/EDL-X/CMX3600.pdf - https://documentation.apple.com/en/finalcutpro/usermanual/index.html#chapter=96%26section=1 @@ -84,13 +91,16 @@ Final Cut Pro X XML Format: Contrib Adapters ---------------- -The contrib area hosts adapters which come from the community (_not_ supported by the core-otio team) and may require extra dependencies. +The contrib area hosts adapters which come from the community (_not_ supported + by the core-otio team) and may require extra dependencies. ### RV Session File ### - Status: write-only adapter supported via the `rv_session` adapter. -- need to set environment variables to locate `py-interp` and `rvSession.py` from within the RV distribution -- set ${OTIO_RV_PYTHON_BIN} to point at `py-interp` from within rv, for example: +- need to set environment variables to locate `py-interp` and `rvSession.py` + from within the RV distribution +- set ${OTIO_RV_PYTHON_BIN} to point at `py-interp` from within rv, for + example: `setenv OTIO_RV_PYTHON_BIN /Applications/RV64.app/Contents/MacOS/py-interp` - set ${OTIO_RV_PYTHON_LIB} to point at the parent directory of `rvSession.py`: `setenv OTIO_RV_PYTHON_LIB /Applications/RV64.app/Contents/src/python` @@ -98,7 +108,8 @@ The contrib area hosts adapters which come from the community (_not_ supported b ### Maya Sequencer ### - Status: supported via the `maya_sequencer` adapter. -- set ${OTIO_MAYA_PYTHON_BIN} to point the location of `mayapy` within the maya installation. +- set ${OTIO_MAYA_PYTHON_BIN} to point the location of `mayapy` within the maya + installation. ### HLS Playlist ### @@ -117,7 +128,9 @@ To build and install the project. Makefile -------- -Even though the project is python, we provide a makefile with some utility targets. These include targets for running unit tests and for running pep8/autopep8 to conform to style guide. To run the target: +Even though the project is python, we provide a makefile with some utility +targets. These include targets for running unit tests and for running +a linter to conform to style guide. To run the target: ```bash # run the unit tests @@ -126,8 +139,6 @@ make test make test VERBOSE=1 # run the code through a linter make lint -# run the code through autopep8. -make autopep8 # generate a coverage report make coverage ``` @@ -135,7 +146,8 @@ make coverage Developing ---------- -Currently the code base is written against python2.7 and python3.5, in keeping with the pep8 style. We ask that before you submit a pull request, you: +Currently the code base is written against python2.7 and python3.5, in keeping +with the pep8 style. We ask that before you submit a pull request, you: - run `make test` -- to ensure that none of the unit tests were broken - run `make lint` -- to conform to pep8 @@ -148,3 +160,5 @@ Contact For more information, please visit http://opentimeline.io/ or https://github.com/PixarAnimationStudios/OpenTimelineIO +or join our announcement mailing list: https://groups.google.com/forum/#!forum/open-timeline-io + diff --git a/bin/otiocat.py b/bin/otiocat.py index fab3af477..c7fb43da7 100755 --- a/bin/otiocat.py +++ b/bin/otiocat.py @@ -1,11 +1,34 @@ #!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Print the contents of an OTIO file to stdout.""" import argparse import opentimelineio as otio -__doc__ = """ Print the contents of an OTIO file to stdout. """ - def _parsed_args(): """ parse commandline arguments with argparse """ @@ -24,19 +47,22 @@ def _parsed_args(): return parser.parse_args() -def _cat_otio_file(fpath): - """Print the JSON for the input file.""" +def _otio_compatible_file_to_json_string(fpath): + """Read the file at fpath with the default otio adapter and return the json + as a string. + """ adapter = otio.adapters.from_name("otio_json") return adapter.write_to_string(otio.adapters.read_from_file(fpath)) def main(): - """Parse arguments and call _cat_otio_file.""" + """Parse arguments and call _otio_compatible_file_to_json_string.""" + args = _parsed_args() for fpath in args.filepath: - print(_cat_otio_file(fpath)) + print(_otio_compatible_file_to_json_string(fpath)) if __name__ == '__main__': diff --git a/bin/otioconvert.py b/bin/otioconvert.py index 42a02d8f2..7aec3f68b 100755 --- a/bin/otioconvert.py +++ b/bin/otioconvert.py @@ -1,6 +1,28 @@ #!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# -# python import argparse import sys @@ -39,14 +61,14 @@ def _parsed_args(): '--input-adapter', type=str, default=None, - help="Instead of inferring the adapter to use, pick from the list.", + help="Explicitly use this adapter for reading the input file", ) parser.add_argument( '-O', '--output-adapter', type=str, default=None, - help="Instead of inferring the adapter to use, pick from the list.", + help="Explicitly use this adapter for writing the output file", ) parser.add_argument( '-T', @@ -60,14 +82,15 @@ def _parsed_args(): '--media-linker', type=str, default=None, - help="Specify a media linker.", + help="Specify a media linker. Default is to use the " + "OTIO_DEFAULT_MEDIA_LINKER, if set.", ) return parser.parse_args() def main(): - """ Parse arguments and convert the files. """ + """Parse arguments and convert the files.""" args = _parsed_args() diff --git a/bin/otioview.py b/bin/otioview.py index a7203b475..bbc9c8157 100755 --- a/bin/otioview.py +++ b/bin/otioview.py @@ -1,15 +1,37 @@ #!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Simple otio viewer""" import os import sys import argparse -import itertools from PySide import QtGui import opentimelineio as otio -import opentimelineioViewWidget as otioViewWidget - -__doc__ = """ Simple otio viewer """ +import opentimelineview as otioViewWidget def _parsed_args(): @@ -17,11 +39,10 @@ def _parsed_args(): description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter ) + parser.add_argument( - '-i', - '--input', + 'input', type=str, - required=False, help='path to input file', ) @@ -35,20 +56,23 @@ def __init__(self, timeline, *args, **kwargs): class Main(QtGui.QMainWindow): - def __init__(self, *args, **kwargs): super(Main, self).__init__(*args, **kwargs) self._current_file = None # window options - self.setWindowTitle('OTIO viewer') + self.setWindowTitle('OpenTimelineIO Viewer') self.resize(900, 500) # widgets - self.sequences = QtGui.QListWidget(parent=self) - self.timeline = otioViewWidget.timeline.Timeline(parent=self) - self.details = otioViewWidget.details.Details(parent=self) + self.sequences_widget = QtGui.QListWidget(parent=self) + self.timeline_widget = otioViewWidget.timeline_widget.Timeline( + parent=self + ) + self.details_widget = otioViewWidget.details_widget.Details( + parent=self + ) # layout splitter = QtGui.QSplitter(parent=self) @@ -57,10 +81,10 @@ def __init__(self, *args, **kwargs): widg = QtGui.QWidget(parent=self) layout = QtGui.QVBoxLayout() widg.setLayout(layout) - layout.addWidget(self.details) - layout.addWidget(self.timeline) + layout.addWidget(self.details_widget) + layout.addWidget(self.timeline_widget) - splitter.addWidget(self.sequences) + splitter.addWidget(self.sequences_widget) splitter.addWidget(widg) splitter.setSizes([200, 700]) @@ -74,23 +98,21 @@ def __init__(self, *args, **kwargs): file_menu.addAction(file_load) # signals - self.sequences.itemSelectionChanged.connect(self._change_sequence) - self.timeline.selection_changed.connect(self.details.set_item) + self.sequences_widget.itemSelectionChanged.connect( + self._change_sequence + ) + self.timeline_widget.selection_changed.connect( + self.details_widget.set_item + ) def _file_load(self): start_folder = None if self._current_file is not None: start_folder = os.path.dirname(self._current_file) - extensions = set( - itertools.chain.from_iterable( - adp.suffixes for adp in otio.plugins.ActiveManifest().adapters - ) - ) + extensions = otio.adapters.suffixes_with_defined_adapters(read=True) - extensions_string = ' '.join( - ('*.{ext}'.format(ext=x) for x in extensions) - ) + extensions_string = ' '.join('*.{}'.format(x) for x in extensions) path = str( QtGui.QFileDialog.getOpenFileName( @@ -106,24 +128,27 @@ def _file_load(self): def load(self, path): self._current_file = path - self.setWindowTitle('OTIO viewer - ' + path) - self.details.set_item(None) - self.sequences.clear() + self.setWindowTitle('OpenTimelineIO View: "{}"'.format(path)) + self.details_widget.set_item(None) + self.sequences_widget.clear() file_contents = otio.adapters.read_from_file(path) + if isinstance(file_contents, otio.schema.Timeline): - self.timeline.set_timeline(file_contents) - self.sequences.setVisible(False) - elif isinstance(file_contents, - otio.schema.SerializeableCollection): + self.timeline_widget.set_timeline(file_contents) + self.sequences_widget.setVisible(False) + elif isinstance( + file_contents, + otio.schema.SerializeableCollection + ): for s in file_contents: - TimelineWidgetItem(s, s.name, self.sequences) - self.sequences.setVisible(True) - self.timeline.set_timeline(None) + TimelineWidgetItem(s, s.name, self.sequences_widget) + self.sequences_widget.setVisible(True) + self.timeline_widget.set_timeline(None) def _change_sequence(self): - selection = self.sequences.selectedItems() + selection = self.sequences_widget.selectedItems() if selection: - self.timeline.set_timeline(selection[0].timeline) + self.timeline_widget.set_timeline(selection[0].timeline) def main(): @@ -136,6 +161,7 @@ def main(): window.load(args.input) window.show() + window.raise_() application.exec_() diff --git a/contrib/adapters/Makefile b/contrib/adapters/Makefile index 90ef9ff3f..2c038ae00 100644 --- a/contrib/adapters/Makefile +++ b/contrib/adapters/Makefile @@ -1,4 +1,4 @@ - +# Makefile for the contrib area export PYTHONPATH:=../../ export OTIO_PLUGIN_MANIFEST_PATH:=contrib_adapters.plugin_manifest.json diff --git a/contrib/adapters/extern_maya_sequencer.py b/contrib/adapters/extern_maya_sequencer.py index 22866eb59..0908b7bb6 100644 --- a/contrib/adapters/extern_maya_sequencer.py +++ b/contrib/adapters/extern_maya_sequencer.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + import os import sys @@ -7,6 +31,7 @@ except ImportError: import urllib.parse as urllib_parse +# import maya and handle standalone mode from maya import cmds try: @@ -17,20 +42,23 @@ import opentimelineio as otio -FPS = {'game': 15, - 'film': 24, - 'pal': 25, - 'ntsc': 30, - 'show': 48, - 'palf': 50, - 'ntscf': 60} +# Mapping of Maya FPS Enum to rate. +FPS = { + 'game': 15, + 'film': 24, + 'pal': 25, + 'ntsc': 30, + 'show': 48, + 'palf': 50, + 'ntscf': 60 +} def _url_to_path(url): if url is None: return None - parsed = urllib_parse.urlparse(url) - return parsed.path + + return urllib_parse.urlparse(url).path def _video_url_for_shot(shot): @@ -54,8 +82,10 @@ def _match_existing_shot(item, existing_shots): url_path = _url_to_path(item.media_reference.target_url) return next( - (shot for shot in existing_shots - if _video_url_for_shot(shot) == url_path), + ( + shot for shot in existing_shots + if _video_url_for_shot(shot) == url_path + ), None ) @@ -101,8 +131,10 @@ def build_sequence(timeline, clean=False): cmds.delete(existing_shots) existing_shots = [] - tracks = [track for track in timeline.tracks - if track.kind == otio.schema.SequenceKind.Video] + tracks = [ + track for track in timeline.tracks + if track.kind == otio.schema.SequenceKind.Video + ] for track_no, track in enumerate(reversed(tracks)): _build_track(track, track_no, existing_shots=existing_shots) @@ -120,7 +152,8 @@ def read_from_file(path, clean=True): def _get_gap(duration): rate = FPS.get(cmds.currentUnit(q=True, time=True), 25) gap_range = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(duration, rate)) + duration=otio.opentime.RationalTime(duration, rate) + ) return otio.schema.Gap(source_range=gap_range) @@ -133,7 +166,7 @@ def _read_shot(shot): target_url=_video_url_for_shot(shot), available_range=otio.opentime.TimeRange( otio.opentime.RationalTime(value=start, rate=rate), - otio.opentime.RationalTime(value=end-start, rate=rate) + otio.opentime.RationalTime(value=end - start, rate=rate) ) ) @@ -142,8 +175,7 @@ def _read_shot(shot): media_reference=video_reference, source_range=otio.opentime.TimeRange( otio.opentime.RationalTime(value=start, rate=rate), - otio.opentime.RationalTime(value=end-start, - rate=rate) + otio.opentime.RationalTime(value=end - start, rate=rate) ) ) @@ -214,12 +246,12 @@ def main(): else: cmds.file(filepath, o=True) sys.stdout.write( - "\nOTIO_JSON_BEGIN\n"+ + "\nOTIO_JSON_BEGIN\n" + otio.adapters.write_to_string( read_sequence(), "otio_json" ) - +"\nOTIO_JSON_END\n" + + "\nOTIO_JSON_END\n" ) cmds.quit(force=True) diff --git a/contrib/adapters/extern_rv.py b/contrib/adapters/extern_rv.py index bc8700c36..5b7b7c923 100755 --- a/contrib/adapters/extern_rv.py +++ b/contrib/adapters/extern_rv.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """RV External Adapter component. Because the rv adapter requires being run from within the RV py-interp to take @@ -18,7 +42,7 @@ # rv import sys.path += [os.path.join(os.environ["OTIO_RV_PYTHON_LIB"], "rvSession")] -import rvSession #noqa +import rvSession # noqa def main(): @@ -56,12 +80,12 @@ def write_otio(otio_obj, to_session): return WRITE_TYPE_MAP[type(otio_obj)](otio_obj, to_session) raise NoMappingForOtioTypeError( - str(type(otio_obj))+ " on object: {}".format(otio_obj) + str(type(otio_obj)) + " on object: {}".format(otio_obj) ) def _write_dissolve(pre_item, in_dissolve, post_item, to_session): - rv_trx = to_session.newNode( "CrossDissolve", str(in_dissolve.name)) + rv_trx = to_session.newNode("CrossDissolve", str(in_dissolve.name)) rv_trx.setProperty( "CrossDissolve", "", @@ -70,7 +94,7 @@ def _write_dissolve(pre_item, in_dissolve, post_item, to_session): rvSession.gto.FLOAT, 1.0 ) - rv_trx.setProperty ( + rv_trx.setProperty( "CrossDissolve", "", "parameters", @@ -92,7 +116,6 @@ def _write_dissolve(pre_item, in_dissolve, post_item, to_session): pre_item.trimmed_range().duration.rate ) - pre_item_rv = write_otio(pre_item, to_session) rv_trx.addInput(pre_item_rv) @@ -101,7 +124,6 @@ def _write_dissolve(pre_item, in_dissolve, post_item, to_session): node_to_insert = post_item_rv if ( - # @TODO: should use "is_missing_reference()"? hasattr(pre_item, "media_reference") and pre_item.media_reference and pre_item.media_reference.available_range and @@ -124,7 +146,6 @@ def _write_dissolve(pre_item, in_dissolve, post_item, to_session): rt_node.addInput(post_item_rv) node_to_insert = rt_node - rv_trx.addInput(node_to_insert) return rv_trx @@ -132,13 +153,18 @@ def _write_dissolve(pre_item, in_dissolve, post_item, to_session): def _write_transition(pre_item, in_trx, post_item, to_session): trx_map = { - otio.schema.TransitionTypes.SMPTE_Dissolve : _write_dissolve, + otio.schema.TransitionTypes.SMPTE_Dissolve: _write_dissolve, } if in_trx.transition_type not in trx_map: return - return trx_map[in_trx.transition_type](pre_item, in_trx, post_item, to_session) + return trx_map[in_trx.transition_type]( + pre_item, + in_trx, + post_item, + to_session + ) def _write_stack(in_stack, to_session): @@ -198,7 +224,7 @@ def _write_item(it, to_session): ) ) - # because OTIO has no global concept of FPS, the rate of the duration is + # because OTIO has no global concept of FPS, the rate of the duration is # used as the rate for the range of the source. # RationalTime.value_rescaled_to returns the time value of the object in # time rate of the argument. diff --git a/contrib/adapters/hls_playlist.py b/contrib/adapters/hls_playlist.py index 93d84c176..275e84644 100644 --- a/contrib/adapters/hls_playlist.py +++ b/contrib/adapters/hls_playlist.py @@ -1,5 +1,28 @@ -''' -HLS Playlist OpenTimelineIO adapter +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""HLS Playlist OpenTimelineIO adapter This adapter supports authoring of HLS playlists within OpenTimelineIO by using clips to represent media fragments. @@ -93,7 +116,7 @@ These values are translated to EXT-X-STREAM-INF and EXT-X-MEDIA attributes as defined in sections 4.3.4.2 and 4.3.4.1 of draft-pantos-http-live-streaming, respectively. -''' +""" import re import copy @@ -115,19 +138,19 @@ except NameError: basestring = str -''' +""" Matches a single key/value pair from an HLS Attribute List. See section 4.2 of draft-pantos-http-live-streaming for more detail. -''' +""" ATTRIBUTE_RE = re.compile( r'(?P[A-Z0-9-]+)' + r'\=' + r'(?P(?:\"[^\r\n"]*\")|[^,]+)' + r',?' ) -''' +""" Matches AttributeValue of the above regex into appropriate data types. Note that these are meant to be joined using regex "or" in this order. -''' +""" _ATTRIBUTE_RE_VALUE_STR_LIST = [ r'(?P(?P[0-9]+)x(?P[0-9]+))\Z', r'(?P0[xX](?P[0-9A-F]+))\Z', @@ -138,17 +161,17 @@ ] ATTRIBUTE_VALUE_RE = re.compile("|".join(_ATTRIBUTE_RE_VALUE_STR_LIST)) -''' +""" Matches a byterange as used in various contexts. See section 4.3.2.2 of draft-pantos-http-live-streaming for an example use of this byterange form. -''' +""" BYTERANGE_RE = re.compile(r'(?P\d+)(?:@(?P\d+))?') -''' +""" Matches HLS Playlist tags or comments, respective. See section 4.1 of draft-pantos-http-live-streaming for more detail. -''' +""" TAG_RE = re.compile( r'#(?PEXT[^:\s]+)(?P:?)(?P.*)' ) @@ -156,15 +179,15 @@ class AttributeListEnum(str): - ''' A subclass allowing us to differentiate enums in HLS attribute lists - ''' + """ A subclass allowing us to differentiate enums in HLS attribute lists + """ def _value_from_raw_attribute_value(raw_attribute_value): - ''' + """ Takes in a raw AttributeValue and returns an appopritate Python type. If there is a problem decoding the value, None is returned. - ''' + """ value_match = ATTRIBUTE_VALUE_RE.match(raw_attribute_value) if not value_match: return None @@ -196,18 +219,18 @@ def _value_from_raw_attribute_value(raw_attribute_value): class AttributeList(dict): - ''' + """ Dictionary-like object representing an HLS AttributeList. See section 4.2 of draft-pantos-http-live-streaming for more detail. - ''' + """ def __init__(self, other=None): - ''' + """ contstructs an :class:`AttributeList`. ``Other`` can be either another dictionary-like object or a list of key/value pairs - ''' + """ if not other: return @@ -220,9 +243,9 @@ def __init__(self, other=None): self[k] = v def __str__(self): - ''' + """ Construct attribute list string as it would exist in an HLS playlist. - ''' + """ attr_list_entries = [] # Use a sorted version of the dictionary to ensure consistency for k, v in sorted(self.items(), key=lambda i: i[0]): @@ -240,11 +263,11 @@ def __str__(self): @classmethod def from_string(cls, attrlist_string): - ''' + """ Accepts an attribute list string and returns an :class:`AttributeList`. The values will be transformed to Python types. - ''' + """ attr_list = cls() match = ATTRIBUTE_RE.search(attrlist_string) while match: @@ -263,13 +286,14 @@ def from_string(cls, attrlist_string): return attr_list + # some special top-levle keys that HLS metadata will be decoded into FORMAT_METADATA_KEY = 'HLS' -''' +""" Some concepts are translatable between HLS and other streaming formats (DASH). These metadata keys are used on OTIO objects outside the HLS namespace because they are higher level concepts. -''' +""" STREAMING_METADATA_KEY = 'streaming' INIT_BYTERANGE_KEY = 'init_byterange' INIT_URI_KEY = 'init_uri' @@ -279,23 +303,20 @@ def from_string(cls, attrlist_string): class Byterange(object): - ''' - Offers interpretation of HLS byte ranges in various forms. - ''' + """Offers interpretation of HLS byte ranges in various forms.""" count = None - '''(:class:`int`) Number of bytes included in the range.''' + """(:class:`int`) Number of bytes included in the range.""" offset = None - '''(:class:`int`) Byte offset at which the range starts.''' + """(:class:`int`) Byte offset at which the range starts.""" def __init__(self, count=None, offset=None): - ''' - Constructs a :class:`Byterange` object. + """Constructs a :class:`Byterange` object. :param count: (:class:`int`) Number of bytes included in the range. :param offset: (:class:`int`) Byte offset at which the range starts. - ''' + """ self.count = (count if count is not None else 0) self.offset = offset @@ -316,7 +337,8 @@ def __repr__(self): ) def __str__(self): - ''' returns a string in HLS format ''' + """returns a string in HLS format""" + out_str = str(self.count) if self.offset is not None: out_str += '@{}'.format(str(self.offset)) @@ -324,11 +346,10 @@ def __str__(self): return out_str def to_dict(self): - ''' - Returns a dict suitable for storing in otio metadata. + """Returns a dict suitable for storing in otio metadata. :return: (:class:`dict`) serializable version of byterange. - ''' + """ range_dict = {BYTE_COUNT_KEY: self.count} if self.offset is not None: range_dict[BYTE_OFFSET_KEY] = self.offset @@ -337,24 +358,23 @@ def to_dict(self): @classmethod def from_string(cls, byterange_string): - ''' - Construct a :class:`Byterange` given a string in HLS format. + """Construct a :class:`Byterange` given a string in HLS format. :param byterange_string: (:class:`str`) a byterange string. :return: (:class:`Byterange`) The instance for the provided string. - ''' + """ m = BYTERANGE_RE.match(byterange_string) return cls.from_match_dict(m.groupdict()) @classmethod def from_match_dict(cls, match_dict): - ''' + """ Construct a :class:`Byterange` given a groupdict from ``BYTERANGE_RE`` :param match_dict: (:class:`dict`) the ``match_dict``. :return: (:class:`Byterange`) The instance for the provided string. - ''' + """ byterange = cls(count=int(match_dict['n'])) try: @@ -366,13 +386,12 @@ def from_match_dict(cls, match_dict): @classmethod def from_dict(cls, info_dict): - ''' - Creates a :class:`Byterange` given a dictionary containing keys like - generated from the :meth:`to_dict method`. + """ Creates a :class:`Byterange` given a dictionary containing keys + like generated from the :meth:`to_dict method`. :param info_dict: (:class:`dict`) Dictionary byterange. :return: (:class:`Byterange`) an equivalent instance. - ''' + """ byterange = cls( count=info_dict.get(BYTE_COUNT_KEY), offset=info_dict.get(BYTE_OFFSET_KEY) @@ -380,7 +399,8 @@ def from_dict(cls, info_dict): return byterange -''' + +""" For a given collection of media, HLS has two playlist types: - Media Playlist - Master Playlist @@ -393,22 +413,22 @@ def from_dict(cls, info_dict): See section 2 of draft-pantos-http-live-streaming for more detail. The constants below define which tags belong to which schema. -''' +""" -''' +""" Basic tags appear in both media and master playlists. See section 4.3.1 of draft-pantos-http-live-streaming for more detail. -''' +""" BASIC_TAGS = set([ "EXTM3U", "EXT-X-VERSION" ]) -''' +""" Media segment tags apply to either the following media or all subsequent segments. They MUST NOT appear in master playlists. See section 4.3.2 of draft-pantos-http-live-streaming for more detail. -''' +""" MEDIA_SEGMENT_TAGS = set([ 'EXTINF', 'EXT-X-BYTERANGE', @@ -419,17 +439,17 @@ def from_dict(cls, info_dict): 'EXT-X-DATERANGE' ]) -''' The subset of above tags that apply to every segment following them ''' +""" The subset of above tags that apply to every segment following them """ MEDIA_SEGMENT_SUBSEQUENT_TAGS = set([ 'EXT-X-KEY', 'EXT-X-MAP', ]) -''' +""" Media Playlist tags must only occur once per playlist, and must not appear in Master Playlists. See section 4.3.3 of draft-pantos-http-live-streaming for more detail. -''' +""" MEDIA_PLAYLIST_TAGS = set([ 'EXT-X-TARGETDURATION', 'EXT-X-MEDIA-SEQUENCE', @@ -439,11 +459,11 @@ def from_dict(cls, info_dict): 'EXT-X-I-FRAMES-ONLY' ]) -''' +""" Master playlist tags declare global parameters for the presentation. They must not appear in media playlists. See section 4.3.4 of draft-pantos-http-live-streaming for more detail. -''' +""" MASTER_PLAYLIST_TAGS = set([ 'EXT-X-MEDIA', 'EXT-X-STREAM-INF', @@ -452,30 +472,30 @@ def from_dict(cls, info_dict): 'EXT-X-SESSION-KEY', ]) -''' +""" Media or Master Playlist tags can appear in either media or master playlists. See section 4.3.5 of draft-pantos-http-live-streaming for more detail. These tags SHOULD appear in either the media or master playlist. If they occur in both, their values MUST agree. These values MUST NOT appear more than once in a playlist. -''' +""" MEDIA_OR_MASTER_TAGS = set([ "EXT-X-INDEPENDENT-SEGMENTS", "EXT-X-START" ]) -''' +""" Some special tags used by the parser. -''' +""" PLAYLIST_START_TAG = "EXTM3U" PLAYLIST_END_TAG = "EXT-X-ENDLIST" PLAYLIST_VERSION_TAG = "EXT-X-VERSION" PLAYLIST_SEGMENT_INF_TAG = "EXTINF" -''' +""" attribute list entries to omit from EXT-I-FRAME-STREAM-INF tags See section 4.3.4.3 of draft-pantos-http-live-streaming for more detail. -''' +""" I_FRAME_OMIT_ATTRS = set([ 'FRAME-RATE', 'AUDIO', @@ -483,72 +503,71 @@ def from_dict(cls, info_dict): 'CLOSED-CAPTIONS' ]) -''' enum for kinds of playlist entries ''' +""" enum for kinds of playlist entries """ EntryType = type('EntryType', (), { 'tag': 'tag', 'comment': 'comment', 'URI': 'URI' }) -''' enum for types of playlists ''' +""" enum for types of playlists """ PlaylistType = type('PlaylistType', (), { 'media': 'media', 'master': 'master' }) -''' mapping from HLS track type to otio ``SequenceKind`` ''' +""" mapping from HLS track type to otio ``SequenceKind`` """ HLS_TRACK_TYPE_TO_OTIO_KIND = { AttributeListEnum('AUDIO'): otio.schema.SequenceKind.Audio, AttributeListEnum('VIDEO'): otio.schema.SequenceKind.Video, # TODO: determine how to handle SUBTITLES and CLOSED-CAPTIONS } -''' mapping from otio ``SequenceKind`` to HLS track type ''' +""" mapping from otio ``SequenceKind`` to HLS track type """ OTIO_TRACK_KIND_TO_HLS_TYPE = dict(( (v, k) for k, v in HLS_TRACK_TYPE_TO_OTIO_KIND.items() )) class HLSPlaylistEntry(object): - ''' - An entry in an HLS playlist. + """An entry in an HLS playlist. Entries can be a tag, a comment, or a URI. All HLS playlists are parsed into lists of :class:`HLSPlaylistEntry` instances that can then be interpreted against the HLS schema. - ''' + """ # TODO: rename this to entry_type to fix builtin masking # type = None - ''' (``EntryType``) the type of entry ''' + """ (``EntryType``) the type of entry """ comment_string = None - ''' + """ (:class:`str`) value of comment (if the ``entry_type`` is ``EntryType.comment``). - ''' + """ tag_name = None - ''' + """ (:class:`str`) Name of tag (if the ``entry_type`` is ``EntryType.tag``). - ''' + """ tag_value = None - ''' + """ (:class:`str`) Value of tag (if the ``entry_type`` is ``EntryType.tag``). - ''' + """ uri = None - ''' + """ (:class:`str`) Value of the URI (if the ``entry_type is ``EntryType.uri``). - ''' + """ def __init__(self, type): - ''' + """ Constructs an :class:`HLSPlaylistEntry`. :param type: (``EntryType``) Type of entry. - ''' + """ self.type = type def __repr__(self): @@ -567,11 +586,11 @@ def __repr__(self): return base_str + ')' def __str__(self): - ''' + """ Returns a string as it would appear in an HLS playlist. :return: (:class:`str`) HLS playlist entry string. - ''' + """ if self.type == EntryType.comment and self.comment_string: return "# {}".format(self.comment_string) elif self.type == EntryType.comment: @@ -588,13 +607,13 @@ def __str__(self): @classmethod def tag_entry(cls, name, value=None): - ''' + """ Creates an ``EntryType.tag`` :class:`HLSPlaylistEntry`. :param name: (:class:`str`) tag name. :param value: (:class:`str`) tag value. :return: (:class:`HLSPlaylistEntry`) Entry instance. - ''' + """ entry = cls(EntryType.tag) entry.tag_name = name entry.tag_value = value @@ -603,12 +622,11 @@ def tag_entry(cls, name, value=None): @classmethod def comment_entry(cls, comment): - ''' - Creates an ``EntryType.comment`` :class:`HLSPlaylistEntry`. + """Creates an ``EntryType.comment`` :class:`HLSPlaylistEntry`. :param comment: (:class:`str`) the comment. :return: (:class:`HLSPlaylistEntry`) Entry instance. - ''' + """ entry = cls(EntryType.comment) entry.comment_string = comment @@ -616,12 +634,11 @@ def comment_entry(cls, comment): @classmethod def uri_entry(cls, uri): - ''' - Creates an ``EntryType.uri`` :class:`HLSPlaylistEntry`. + """Creates an ``EntryType.uri`` :class:`HLSPlaylistEntry`. :param uri: (:class:`str`) A URI string. :return: (:class:`HLSPlaylistEntry`) Entry instance. - ''' + """ entry = cls(EntryType.URI) entry.uri = uri @@ -629,13 +646,12 @@ def uri_entry(cls, uri): @classmethod def from_string(cls, entry_string): - ''' - Creates an `:class:`HLSPlaylistEntry` given a string as it appears in - an HLS playlist. + """Creates an `:class:`HLSPlaylistEntry` given a string as it appears + in an HLS playlist. :param entry_string: (:class:`str`) String from an HLS playlist. :return: (:class:`HLSPlaylistEntry`) Entry instance. - ''' + """ # Empty lines are skipped if not entry_string.strip(): return None @@ -644,12 +660,11 @@ def from_string(cls, entry_string): m = TAG_RE.match(entry_string) if m: group_dict = m.groupdict() - tag_value = (group_dict['tagvalue'] if - group_dict['hasvalue'] else None) - entry = cls.tag_entry( - group_dict['tagname'], - tag_value + tag_value = ( + group_dict['tagvalue'] + if group_dict['hasvalue'] else None ) + entry = cls.tag_entry(group_dict['tagname'], tag_value) return entry # Attempt to parse as a comment @@ -663,9 +678,7 @@ def from_string(cls, entry_string): return entry - ''' - A dispatch dictionary for grabbing the right Regex to parse tags. - ''' + """A dispatch dictionary for grabbing the right Regex to parse tags.""" TAG_VALUE_RE_MAP = { "EXTINF": re.compile(r'(?P\d+(\.\d*)?),(?P.*$)'), "EXT-X-BYTERANGE": BYTERANGE_RE, @@ -677,8 +690,7 @@ def from_string(cls, entry_string): } def parsed_tag_value(self, playlist_version=None): - ''' - Parses and returns ``self.tag_value`` based on the HLS schema. + """Parses and returns ``self.tag_value`` based on the HLS schema. The value will be a dictionary where the keys are the names used in the draft Pantos HTTP Live Streaming doc. When "attribute-list" is @@ -688,7 +700,7 @@ def parsed_tag_value(self, playlist_version=None): :param playlist_version: (:class:`int`) version number of the playlist. If none is provided, a best guess will be made. :return: The parsed value. - ''' + """ if self.type != EntryType.tag: return None @@ -716,10 +728,9 @@ def parsed_tag_value(self, playlist_version=None): class HLSPlaylistParser(object): - ''' - Bootstraps HLS parsing and hands the playlist string off to the appropriate - parser for the type - ''' + """Bootstraps HLS parsing and hands the playlist string off to the + appropriate parser for the type + """ def __init__(self, edl_string): self.timeline = otio.schema.Timeline() @@ -728,9 +739,7 @@ def __init__(self, edl_string): self._parse_playlist(edl_string) def _parse_playlist(self, edl_string): - ''' - Parses the HLS Playlist string line-by-line. - ''' + """Parses the HLS Playlist string line-by-line.""" # parse lines until we encounter one that identifies the playlist type # then hand off start_encountered = False @@ -791,9 +800,7 @@ def _parse_playlist(self, edl_string): class MediaPlaylistParser(object): - ''' - Parses an HLS Media playlist returning a sequence - ''' + """Parses an HLS Media playlist returning a sequence""" def __init__(self, playlist_entries, playlist_version=None): self.sequence = otio.schema.Sequence( @@ -803,21 +810,18 @@ def __init__(self, playlist_entries, playlist_version=None): self._parse_entries(playlist_entries, playlist_version) def _handle_sequence_metadata(self, entry, playlist_version, clip): - ''' - Stashes the tag value in the sequence metadata - ''' + """Stashes the tag value in the sequence metadata""" value = entry.tag_value self.sequence.metadata[FORMAT_METADATA_KEY][entry.tag_name] = value def _handle_discarded_metadata(self, entry, playlist_version, clip): - ''' - Handler for tags that are discarded. This is done when a tag's + """Handler for tags that are discarded. This is done when a tag's information is represented by the native OTIO concepts. For instance, the EXT-X-TARGETDURATION tag simply gives a rounded value for the maximum segment size in the playlist. This can easily be found in OTIO by examining the clips. - ''' + """ # Do nothing def _metadata_dict_for_MAP(self, entry, playlist_version): @@ -861,9 +865,9 @@ def _handle_BYTERANGE(self, entry, playlist_version, clip): ) ref_streaming_metadata.update(byterange.to_dict()) - ''' + """ Specifies handlers for specific HLS tags. - ''' + """ TAG_HANDLERS = { "EXTINF": _handle_INF, PLAYLIST_VERSION_TAG: _handle_sequence_metadata, @@ -875,7 +879,7 @@ def _handle_BYTERANGE(self, entry, playlist_version, clip): } def _parse_entries(self, playlist_entries, playlist_version): - ''' interpret the entries through the lens of the schema ''' + """Interpret the entries through the lens of the schema""" current_media_ref = otio.media_reference.External( metadata={ FORMAT_METADATA_KEY: {}, @@ -958,7 +962,8 @@ def _parse_entries(self, playlist_entries, playlist_version): hls_metadata = self.sequence.metadata[FORMAT_METADATA_KEY] hls_metadata[entry.tag_name] = entry.tag_value -''' + +""" Compatibility version list: EXT-X-BYTERANGE >= 4 EXT-X-I-FRAMES-ONLY >= 4 @@ -972,7 +977,7 @@ def _parse_entries(self, playlist_entries, playlist_version): master playlist: EXT-X-MEDIA with INSTREAM-ID="SERVICE" -''' +""" def entries_for_segment( @@ -982,8 +987,7 @@ def entries_for_segment( segment_byterange=None, segment_tags=None ): - ''' - Creates a set of :class:`HLSPlaylistEntries` with the given parameters. + """Creates a set of :class:`HLSPlaylistEntries` with the given parameters. :param uri: (:class:`str`) The uri for the segment media. :param segment_duration: (:class:`opentimelineio.opentime.RationalTime`) @@ -995,7 +999,7 @@ def entries_for_segment( :return: (:class:`list`) a group of :class:`HLSPlaylistEntry` instances for the segment - ''' + """ # Create the tags dict to build if segment_tags: tags = copy.deepcopy(segment_tags) @@ -1038,14 +1042,13 @@ def entries_for_segment( def stream_inf_attr_list_for_track(track): - ''' - Builds an :class:`AttributeList` instance for use in ``STREAM-INF`` tags - for the provided track. + """ Builds an :class:`AttributeList` instance for use in ``STREAM-INF`` + tags for the provided track. :param track: (:class:`otio.schema.Sequence`) A track representing a variant stream :return: (:class:`AttributeList`) The instance from the metadata - ''' + """ streaming_metadata = track.metadata.get(STREAMING_METADATA_KEY, {}) attributes = [] @@ -1074,9 +1077,8 @@ def stream_inf_attr_list_for_track(track): def master_playlist_to_string(master_timeline): - ''' - Writes a master playlist describing the tracks - ''' + """Writes a master playlist describing the tracks""" + # start with a version number of 1, as features are encountered, we will # update the version accordingly version_requirements = set([1]) @@ -1135,12 +1137,14 @@ def master_playlist_to_string(master_timeline): track_uri = media_playlist_default_uri # Build the attribute list - attributes = AttributeList([ - ('TYPE', hls_type), - ('GROUP-ID', group_id), - ('URI', track_uri), - ('NAME', track.name), - ]) + attributes = AttributeList( + [ + ('TYPE', hls_type), + ('GROUP-ID', group_id), + ('URI', track_uri), + ('NAME', track.name), + ] + ) if streaming_metadata.get('autoselect'): attributes['AUTOSELECT'] = AttributeListEnum('YES') @@ -1313,19 +1317,19 @@ def __init__( self._build_playlist_with_sequence(media_sequence) def _build_playlist_with_sequence(self, media_sequence): - ''' + """ Executes methods to result in a fully populated _playlist_entries list - ''' + """ self._copy_HLS_metadata(media_sequence) self._setup_sequence_info(media_sequence) self._add_segment_entries(media_sequence) self._finalize_entries(media_sequence) def _copy_HLS_metadata(self, media_sequence): - ''' + """ Copies any metadata in the "HLS" namespace from the sequence to the playlist-global tags - ''' + """ # Grab any metadata provided on the otio try: sequence_metadata = media_sequence.metadata[FORMAT_METADATA_KEY] @@ -1347,9 +1351,8 @@ def _copy_HLS_metadata(self, media_sequence): pass def _setup_sequence_info(self, media_sequence): - ''' - sets up playlist global metadata - ''' + """sets up playlist global metadata""" + # Setup the sequence start if 'EXT-X-I-FRAMES-ONLY' in media_sequence.metadata.get( FORMAT_METADATA_KEY, @@ -1369,20 +1372,21 @@ def _setup_sequence_info(self, media_sequence): # If we found a sequence start or one isn't already set in the # metadata, create the tag for it. - if (sequence_start is not None or - 'EXT-X-MEDIA-SEQUENCE' not in self._playlist_tags): - + if ( + sequence_start is not None + or 'EXT-X-MEDIA-SEQUENCE' not in self._playlist_tags + ): # Choose a reasonable sequence start default if sequence_start is None: sequence_start = 1 self._playlist_tags['EXT-X-MEDIA-SEQUENCE'] = str(sequence_start) def _add_map_entry(self, fragment): - ''' - adds an EXT-X-MAP entry from the given fragment + """adds an EXT-X-MAP entry from the given fragment returns the added entry - ''' + """ + media_ref = fragment.media_reference # Extract useful tag data @@ -1406,10 +1410,7 @@ def _add_map_entry(self, fragment): # Construct the entry with the attrlist as the value map_tag_str = str(map_attr_list) - entry = HLSPlaylistEntry.tag_entry( - "EXT-X-MAP", - map_tag_str - ) + entry = HLSPlaylistEntry.tag_entry("EXT-X-MAP", map_tag_str) self._playlist_entries.append(entry) @@ -1421,7 +1422,7 @@ def _add_entries_for_segment_from_fragments( omit_hls_keys=None, is_iframe_playlist=False ): - ''' + """ For the given list of otio clips representing fragments in the mp4, add playlist entries for single HLS segment. @@ -1434,7 +1435,7 @@ def _add_entries_for_segment_from_fragments( :return: (:class:`list` the :class:`HLSPlaylistEntry` instances added to the playlist - ''' + """ if is_iframe_playlist: entries = [] for fragment in fragments: @@ -1526,10 +1527,10 @@ def _add_entries_for_segment_from_fragments( return segment_entries def _fragments_have_same_map(self, fragment, following_fragment): - ''' + """ Given fragment and following_fragment, returns whether or not their initialization data is the same (what becomes EXT-X-MAP) - ''' + """ media_ref = fragment.media_reference media_ref_streaming_md = media_ref.metadata.get( STREAMING_METADATA_KEY, @@ -1557,8 +1558,8 @@ def _fragments_have_same_map(self, fragment, following_fragment): Byterange.from_dict(init_dict) if init_dict else dummy_range ) following_range = ( - Byterange.from_dict(following_init_dict) if following_init_dict - else dummy_range + Byterange.from_dict(following_init_dict) + if following_init_dict else dummy_range ) if init_range != following_range: @@ -1567,9 +1568,9 @@ def _fragments_have_same_map(self, fragment, following_fragment): return True def _fragments_are_contiguous(self, fragment, following_fragment): - ''' - Given fragment and following_fragment (otio clips) returns whether or - not they are contiguous. + """ Given fragment and following_fragment (otio clips) returns whether + or not they are contiguous. + To be contiguous the fragments must: 1. have the same file URL 2. have the same initialization data (what becomes EXT-X-MAP) @@ -1577,7 +1578,7 @@ def _fragments_are_contiguous(self, fragment, following_fragment): follows fragment's last byte) Returns True if following_fragment is contiguous from fragment - ''' + """ # Fragments are contiguous if: # 1. They have the file url # 2. They have the same map info @@ -1596,8 +1597,8 @@ def _fragments_are_contiguous(self, fragment, following_fragment): return False if ( - media_ref_streaming_md.get(INIT_URI_KEY) != - following_ref_streaming_md.get(INIT_URI_KEY) + media_ref_streaming_md.get(INIT_URI_KEY) != + following_ref_streaming_md.get(INIT_URI_KEY) ): return False @@ -1619,9 +1620,8 @@ def _fragments_are_contiguous(self, fragment, following_fragment): return True def _add_segment_entries(self, media_sequence): - ''' - given a media sequence, generates the segment entries - ''' + """given a media sequence, generates the segment entries""" + # Determine whether or not this is an I-Frame playlist sequence_hls_metadata = media_sequence.metadata.get('HLS') is_iframe_playlist = 'EXT-X-I-FRAMES-ONLY' in sequence_hls_metadata @@ -1700,9 +1700,8 @@ def _add_segment_entries(self, media_sequence): self._playlist_tags['EXT-X-TARGETDURATION'] = str(int(max_duration)) def _finalize_entries(self, media_sequence): - ''' - Does final wrap-up of playlist entries - ''' + """Does final wrap-up of playlist entries""" + self._playlist_tags['EXT-X-PLAYLIST-TYPE'] = 'VOD' # add the end @@ -1724,8 +1723,8 @@ def _finalize_entries(self, media_sequence): # add in the rest of the header entries in a deterministic order playlist_header_entries += ( - HLSPlaylistEntry.tag_entry(k, v) for k, v in - sorted(self._playlist_tags.items(), key=lambda i: i[0]) + HLSPlaylistEntry.tag_entry(k, v) + for k, v in sorted(self._playlist_tags.items(), key=lambda i: i[0]) ) # Prepend the entries with the header entries @@ -1734,9 +1733,8 @@ def _finalize_entries(self, media_sequence): ) def playlist_string(self): - ''' - Returns the string representation of the playlist entries - ''' + """Returns the string representation of the playlist entries""" + return '\n'.join( (str(entry) for entry in self._playlist_entries) ) @@ -1745,17 +1743,15 @@ def playlist_string(self): def read_from_string(input_str): - ''' - Adapter entry point for reading. - ''' + """Adapter entry point for reading.""" + parser = HLSPlaylistParser(input_str) return parser.timeline def write_to_string(input_otio): - ''' - Adapter entry point for writing. - ''' + """Adapter entry point for writing.""" + if len(input_otio.tracks) == 0: return None diff --git a/contrib/adapters/maya_sequencer.py b/contrib/adapters/maya_sequencer.py index 8627c2be8..06b0cb201 100644 --- a/contrib/adapters/maya_sequencer.py +++ b/contrib/adapters/maya_sequencer.py @@ -1,10 +1,35 @@ -""" Maya Sequencer Adapter Harness """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Maya Sequencer Adapter Harness""" import os import subprocess from .. import adapters + def write_to_file(input_otio, filepath): if "OTIO_MAYA_PYTHON_BIN" not in os.environ: raise RuntimeError( @@ -29,7 +54,7 @@ def write_to_file(input_otio, filepath): os.environ['PYTHONPATH'] = ( os.pathsep.join( [ - os.environ.setdefault('PYTHONPATH', '') , + os.environ.setdefault('PYTHONPATH', ''), os.path.dirname(__file__) ] ) @@ -57,6 +82,7 @@ def write_to_file(input_otio, filepath): "file adapter) failed. stderr output: " + err ) + def read_from_file(filepath): if "OTIO_MAYA_PYTHON_BIN" not in os.environ: raise RuntimeError( @@ -64,11 +90,10 @@ def read_from_file(filepath): "mayapy within the Maya installation." ) - os.environ['PYTHONPATH'] = ( os.pathsep.join( [ - os.environ.setdefault('PYTHONPATH', '') , + os.environ.setdefault('PYTHONPATH', ''), os.path.dirname(__file__) ] ) @@ -105,4 +130,3 @@ def read_from_file(filepath): "file adapter) failed. stderr output: " + err ) return result - diff --git a/contrib/adapters/rv.py b/contrib/adapters/rv.py index 6b00cd000..699bd6b20 100644 --- a/contrib/adapters/rv.py +++ b/contrib/adapters/rv.py @@ -1,4 +1,28 @@ -""" RvSession Adapter harness """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""RvSession Adapter harness""" import subprocess import os @@ -24,7 +48,7 @@ def write_to_file(input_otio, filepath): os.environ['PYTHONPATH'] = ( os.pathsep.join( [ - os.environ.setdefault('PYTHONPATH', '') , + os.environ.setdefault('PYTHONPATH', ''), os.path.dirname(__file__) ] ) @@ -46,9 +70,9 @@ def write_to_file(input_otio, filepath): out, err = proc.communicate() if out.strip(): - print "stdout: ", out + print("stdout: {}".format(out)) if err.strip(): - print "stderr: ", err + print("stderr: {}".format(err)) if proc.returncode: raise RuntimeError( diff --git a/contrib/adapters/tests/test_hls_playlist_adapter.py b/contrib/adapters/tests/test_hls_playlist_adapter.py index 90a9aad79..04505bb7f 100644 --- a/contrib/adapters/tests/test_hls_playlist_adapter.py +++ b/contrib/adapters/tests/test_hls_playlist_adapter.py @@ -1,4 +1,27 @@ -import json +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + import os import tempfile import unittest @@ -13,7 +36,7 @@ # Load the adapter module using otio hls_playlist = otio.adapters.from_name("hls_playlist").module() -MEM_PLAYLIST_REF_VALUE = '''#EXTM3U +MEM_PLAYLIST_REF_VALUE = """#EXTM3U #EXT-X-VERSION:7 #EXT-X-INDEPENDENT-SEGMENTS #EXT-X-MEDIA-SEQUENCE:1 @@ -23,22 +46,22 @@ #EXTINF:2.00200, #EXT-X-BYTERANGE:534220@1361 video1.mp4 -#EXT-X-ENDLIST''' +#EXT-X-ENDLIST""" -MEM_MASTER_PLAYLIST_REF_VALUE = '''#EXTM3U +MEM_MASTER_PLAYLIST_REF_VALUE = """#EXTM3U #EXT-X-VERSION:6 #EXT-X-MEDIA:GROUP-ID="aud1",NAME="a1",TYPE=AUDIO,URI="a1/prog_index.m3u8" #EXT-X-STREAM-INF:AUDIO="aud1",BANDWIDTH=135801,CODECS="avc.test,aac.test",FRAME-RATE=23.976,RESOLUTION=1920x1080 -v1/prog_index.m3u8''' +v1/prog_index.m3u8""" -MEM_IFRAME_MASTER_PLAYLIST_REF_VALUE = '''#EXTM3U +MEM_IFRAME_MASTER_PLAYLIST_REF_VALUE = """#EXTM3U #EXT-X-VERSION:6 #EXT-X-MEDIA:GROUP-ID="aud1",NAME="a1",TYPE=AUDIO,URI="a1/prog_index.m3u8" #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=123456,CODECS="avc.test",RESOLUTION=1920x1080,URI="v1/iframe_index.m3u8" #EXT-X-STREAM-INF:AUDIO="aud1",BANDWIDTH=135801,CODECS="avc.test,aac.test",FRAME-RATE=23.976,RESOLUTION=1920x1080 -v1/prog_index.m3u8''' +v1/prog_index.m3u8""" -MEM_COMPLEX_MASTER_PLAYLIST_REF_VALUE = '''#EXTM3U +MEM_COMPLEX_MASTER_PLAYLIST_REF_VALUE = """#EXTM3U #EXT-X-VERSION:6 #EXT-X-MEDIA:GROUP-ID="aud1",NAME="a1",TYPE=AUDIO,URI="a1/prog_index.m3u8" #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=123456,CODECS="avc.test",RESOLUTION=1920x1080,URI="v1/iframe_index.m3u8" @@ -46,13 +69,13 @@ #EXT-X-STREAM-INF:AUDIO="aud1",BANDWIDTH=135801,CODECS="avc.test,aac.test",FRAME-RATE=23.976,RESOLUTION=1920x1080 v1/prog_index.m3u8 #EXT-X-STREAM-INF:AUDIO="aud1",BANDWIDTH=24690,CODECS="avc.test,aac.test",FRAME-RATE=23.976,RESOLUTION=720x480 -v2/prog_index.m3u8''' +v2/prog_index.m3u8""" -MEM_SINGLE_TRACK_MASTER_PLAYLIST_REF_VALUE = '''#EXTM3U +MEM_SINGLE_TRACK_MASTER_PLAYLIST_REF_VALUE = """#EXTM3U #EXT-X-VERSION:6 #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=123456,CODECS="avc.test",RESOLUTION=1920x1080,URI="v1/iframe_index.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=123456,CODECS="avc.test",FRAME-RATE=23.976,RESOLUTION=1920x1080 -v1/prog_index.m3u8''' +v1/prog_index.m3u8""" class HLSPlaylistDataStructuresTest(unittest.TestCase): @@ -472,7 +495,7 @@ def test_master_pl_with_iframe_pl_from_mem(self): os.remove(media_pl_tmp_path) # Drop blank lines before comparing - pl_string = '\n'.join((line for line in pl_string.split('\n') if line)) + pl_string = '\n'.join(line for line in pl_string.split('\n') if line) # Compare against the reference value self.assertEqual(pl_string, MEM_IFRAME_MASTER_PLAYLIST_REF_VALUE) @@ -557,16 +580,16 @@ def test_master_pl_complex_from_mem(self): os.remove(media_pl_tmp_path) # Drop blank lines before comparing - pl_string = '\n'.join((line for line in pl_string.split('\n') if line)) + pl_string = '\n'.join(line for line in pl_string.split('\n') if line) # Compare against the reference value self.assertEqual(pl_string, MEM_COMPLEX_MASTER_PLAYLIST_REF_VALUE) def test_master_playlist_hint_metadata(self): - ''' + """ Test that URL hints for master playlists don't leak out to media playlsits. - ''' + """ # Start with the reference playlist hls_path = HLS_EXAMPLE_PATH timeline = otio.adapters.read_from_file(hls_path) @@ -604,9 +627,9 @@ def test_master_playlist_hint_metadata(self): self.assertFalse(line.startswith('#iframe_uri:')) def test_explicit_master_pl_from_mem(self): - ''' - Test that forcing a master playlist for a single track timeline works. - ''' + """Test that forcing a master playlist for a single track timeline + works. + """ t = otio.schema.Timeline() # Set the master playlist flag t.metadata.update( diff --git a/contrib/adapters/tests/test_maya_sequencer.py b/contrib/adapters/tests/test_maya_sequencer.py index ca76aeee3..1dbdf8ce7 100644 --- a/contrib/adapters/tests/test_maya_sequencer.py +++ b/contrib/adapters/tests/test_maya_sequencer.py @@ -1,4 +1,28 @@ -""" unit tests for the maya sequencer adapter """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Unit tests for the maya sequencer adapter""" import os import tempfile @@ -15,17 +39,17 @@ SCREENING_EXAMPLE_PATH = os.path.join(SAMPLE_DATA_DIR, "screening_example.edl") SAMPLE_DATA_DIR = os.path.join(os.path.dirname(__file__), "sample_data") BASELINE_PATH = os.path.join(SAMPLE_DATA_DIR, "screening_example.ma") -SETATTR_TO_CHECK = (".ef",".sf",".sn",".se",".ssf") +SETATTR_TO_CHECK = (".ef", ".sf", ".sn", ".se", ".ssf") def filter_maya_file(contents): return '\n'.join( - l for l in contents.split('\n') + l for l in contents.split('\n') if ( - l.strip().startswith('setAttr') + l.strip().startswith('setAttr') and any(a in l for a in SETATTR_TO_CHECK) or ( - not l.startswith('//') + not l.startswith('//') and not l.startswith('requires') and not l.startswith('fileInfo') and not l.startswith('currentUnit') diff --git a/contrib/adapters/tests/test_rvsession.py b/contrib/adapters/tests/test_rvsession.py index f896a34ca..a9ea4f032 100644 --- a/contrib/adapters/tests/test_rvsession.py +++ b/contrib/adapters/tests/test_rvsession.py @@ -1,4 +1,28 @@ -""" unit tests for the rv session file adapter """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Unit tests for the rv session file adapter""" import os import tempfile @@ -7,7 +31,10 @@ import opentimelineio as otio SAMPLE_DATA_DIR = os.path.join( - os.path.dirname(otio.__file__),"..","tests", "sample_data" + os.path.dirname(otio.__file__), + "..", + "tests", + "sample_data" ) SCREENING_EXAMPLE_PATH = os.path.join(SAMPLE_DATA_DIR, "screening_example.edl") TRANSITION_EXAMPLE_PATH = os.path.join(SAMPLE_DATA_DIR, "transition_test.otio") @@ -16,6 +43,61 @@ BASELINE_TRANSITION_PATH = os.path.join(SAMPLE_DATA_DIR, "transition_test.rv") +SAMPLE_DATA = """{ + "OTIO_SCHEMA": "Timeline.1", + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "children": [{ + "OTIO_SCHEMA": "Sequence.1", + "kind": "Video", + "children": [{ + "OTIO_SCHEMA": "Gap.1", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 10.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 0.0 + } + } + }, + { + "OTIO_SCHEMA": "Transition.1", + "transition_type": "SMPTE_Dissolve", + "in_offset": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 10.0 + }, + "out_offset": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 10.0 + } + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "MissingReference.1" + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 10.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, "value": 10.0 + } + } + }] + }] + } +}""" + + @unittest.skipIf( "OTIO_RV_PYTHON_LIB" not in os.environ or "OTIO_RV_PYTHON_BIN" not in os.environ, @@ -56,39 +138,7 @@ def test_transition_rvsession_read(self): def test_transition_rvsession_covers_entire_shots(self): # SETUP - timeline = otio.adapters.read_from_string("""{ - "OTIO_SCHEMA": "Timeline.1", - "tracks": { - "OTIO_SCHEMA": "Stack.1", - "children": [{ - "OTIO_SCHEMA": "Sequence.1", - "kind": "Video", - "children": [{ - "OTIO_SCHEMA": "Gap.1", - "source_range": { - "OTIO_SCHEMA": "TimeRange.1", - "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 10.0 }, - "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 0.0 } - } - }, { - "OTIO_SCHEMA": "Transition.1", - "transition_type": "SMPTE_Dissolve", - "in_offset": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 10.0 }, - "out_offset": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 10.0 } - }, { - "OTIO_SCHEMA": "Clip.1", - "media_reference": { - "OTIO_SCHEMA": "MissingReference.1" - }, - "source_range": { - "OTIO_SCHEMA": "TimeRange.1", - "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 10.0 }, - "start_time": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24.0, "value": 10.0 } - } - }] - }] - } - }""", "otio_json") + timeline = otio.adapters.read_from_string(SAMPLE_DATA, "otio_json") tmp_path = tempfile.mkstemp(suffix=".rv", text=True)[1] # EXERCISE diff --git a/doc/source/conf.py b/doc/source/conf.py index c6b3a44bd..9c6296c5b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -53,7 +53,7 @@ # General information about the project. project = u'OpenTimelineIO' -copyright = u'2016, Pixar Animation Studios' +copyright = u'2017, Pixar Animation Studios' author = u'Pixar Animation Studios' # The version info for the project you're documenting, acts as replacement for @@ -63,7 +63,7 @@ # The short X.Y version. version = u'0.5' # The full version, including alpha/beta/rc tags. -release = u'Alpha5' +release = u'Beta5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -138,7 +138,7 @@ # The name for this set of Sphinx documents. # "<project> v<release> documentation" by default. # -# html_title = u'OpenTimelineIO vAlpha5' +# html_title = u'OpenTimelineIO vBeta5' # A shorter title for the navigation bar. Default is the same as html_title. # @@ -150,8 +150,8 @@ # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of -# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. +# the docs. This file should be a Windows icon file (.ico) being 16x16 or +# 32x32 pixels large. # # html_favicon = None @@ -322,7 +322,9 @@ # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'OpenTimelineIO', u'OpenTimelineIO Documentation', - author, 'OpenTimelineIO', 'One line description of project.', + author, 'OpenTimelineIO', + 'Open Source API and interchange format for editorial timeline' + ' information.', 'Miscellaneous'), ] diff --git a/examples/conform.py b/examples/conform.py index 2a92420c2..7d7d5ef9b 100755 --- a/examples/conform.py +++ b/examples/conform.py @@ -1,7 +1,29 @@ #!/usr/bin/env python - -""" -Example OTIO script that reads a timeline and then relinks clips +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Example OTIO script that reads a timeline and then relinks clips to movie files found in a given folder, based on matching names. Demo: @@ -33,8 +55,7 @@ def parse_args(): """ parse arguments out of sys.argv """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( - '-i', - '--input', + 'input', type=str, required=True, help='Timeline file(s) to read. Any format supported by OTIO will' @@ -58,7 +79,7 @@ def parse_args(): def _find_matching_media(name, folder): - """ Look for media with this name in this folder. """ + """Look for media with this name in this folder.""" # In this case we're looking in the filesystem. # In your case, you might want to look in your asset management system @@ -89,6 +110,7 @@ def _find_matching_media(name, folder): def _conform_timeline(timeline, folder): """ Look for replacement media for each clip in the given timeline. + The clips are relinked in place if media with a matching name is found. """ diff --git a/examples/shot_detect.py b/examples/shot_detect.py index 724c1375d..c902ff279 100755 --- a/examples/shot_detect.py +++ b/examples/shot_detect.py @@ -1,7 +1,29 @@ #!/usr/bin/env python - -""" -Example OTIO script that generates an OTIO from a single quicktime by using +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Example OTIO script that generates an OTIO from a single quicktime by using ffprobe to detect shot breaks. """ diff --git a/opentimelineio/__init__.py b/opentimelineio/__init__.py index e8ca69501..cdd74c5bd 100644 --- a/opentimelineio/__init__.py +++ b/opentimelineio/__init__.py @@ -1,4 +1,30 @@ -""" An editorial interchange format and library. +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""An editorial interchange format and library. + +see: http://opentimeline.io .. moduleauthor:: Pixar Animation Studios <opentimelineio@pixar.com> """ diff --git a/opentimelineio/adapters/__init__.py b/opentimelineio/adapters/__init__.py index af9cd3637..de86ebfdb 100644 --- a/opentimelineio/adapters/__init__.py +++ b/opentimelineio/adapters/__init__.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Expose the adapter interface to developers. To read from an existing representation, use the read_from_string and @@ -22,14 +46,25 @@ from .adapter import Adapter # noqa -def suffixes_with_defined_adapters(): - """ - Return a set of all the suffixes that have adapters defined for them. - """ +def suffixes_with_defined_adapters(read=False, write=False): + """Return a set of all the suffixes that have adapters defined for them.""" + + if not read and not write: + read = True + write = True + + positive_adapters = [] + for adp in plugins.ActiveManifest().adapters: + if read and adp.has_feature("read"): + positive_adapters.append(adp) + continue + + if write and adp.has_feature("write"): + positive_adapters.append(adp) return set( itertools.chain.from_iterable( - adp.suffixes for adp in plugins.ActiveManifest().adapters + adp.suffixes for adp in positive_adapters ) ) diff --git a/opentimelineio/adapters/adapter.py b/opentimelineio/adapters/adapter.py index 470557127..8ee4a59bb 100644 --- a/opentimelineio/adapters/adapter.py +++ b/opentimelineio/adapters/adapter.py @@ -1,7 +1,31 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Implementation of the OTIO internal `Adapter` system. For information on writing adapters, please consult: - https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter + https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter # noqa """ from .. import ( @@ -28,7 +52,7 @@ class Adapter(plugins.PythonPlugin): for OTIO. For more information: - https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter + https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter # noqa """ _serializeable_label = "Adapter.1" @@ -56,6 +80,25 @@ def __init__( doc="File suffixes associated with this adapter." ) + def has_feature(self, feature_string): + """ + return true if adapter supports feature_string, which must be a key + of the _FEATURE_MAP dictionary. + + Will trigger a call to self.module(), which imports the plugin. + """ + + if feature_string.lower() not in _FEATURE_MAP.keys(): + return False + + search_strs = _FEATURE_MAP[feature_string] + + try: + return any(hasattr(self.module(), s) for s in search_strs) + except ImportError: + # @TODO: should issue a warning that the plugin was not importable? + return False + def read_from_file( self, filepath, @@ -74,8 +117,8 @@ def read_from_file( result = None if ( - not hasattr(self.module(), "read_from_file") and - hasattr(self.module(), "read_from_string") + not self.has_feature("read_from_file") and + self.has_feature("read_from_string") ): with open(filepath, 'r') as fo: contents = fo.read() @@ -108,8 +151,8 @@ def write_to_file(self, input_otio, filepath): """ if ( - not hasattr(self.module(), "write_to_file") and - hasattr(self.module(), "write_to_string") + not self.has_feature("write_to_file") and + self.has_feature("write_to_string") ): result = self.write_to_string(input_otio) with open(filepath, 'w') as fo: @@ -187,7 +230,7 @@ def _with_linked_media_references( media_linker_name, media_linker_argument_map ): - """ Link media references in the read_otio if possible. + """Link media references in the read_otio if possible. Makes changes in place and returns the read_otio structure back. """ @@ -212,3 +255,14 @@ def _with_linked_media_references( cl.media_reference = new_mr return read_otio + + +# map of attr to look for vs feature name in the adapter plugin +_FEATURE_MAP = { + 'read_from_file': ['read_from_file'], + 'read_from_string': ['read_from_string'], + 'read': ['read_from_file', 'read_from_string'], + 'write_to_file': ['write_to_file'], + 'write_to_string': ['write_to_string'], + 'write': ['write_to_file', 'write_to_string'] +} diff --git a/opentimelineio/adapters/cmx_3600.py b/opentimelineio/adapters/cmx_3600.py index 8d1f5ddff..e4aee9eda 100644 --- a/opentimelineio/adapters/cmx_3600.py +++ b/opentimelineio/adapters/cmx_3600.py @@ -1,4 +1,29 @@ -# OpenTimelineIO CMX 3600 EDL Adapter +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""OpenTimelineIO CMX 3600 EDL Adapter""" + # Note: this adapter is not an ideal model for new adapters, but it works. # If you want to write your own adapter, please see: # https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter @@ -17,6 +42,7 @@ # these are all CMX_3600 transition codes # the wipe is written in regex format because it is W### where the ### is # a 'wipe code' +# @TODO: not currently read by the transition code transition_regex_map = { 'C': 'cut', 'D': 'dissolve', @@ -30,15 +56,16 @@ # We name the actual tracks V and A1,A2,A3,etc. # This channel_map tells you which track to use for each channel shorthand. # Channels not listed here are used as track names verbatim. -channel_map = {'A': ['A1'], - 'AA': ['A1', 'A2'], - 'B': ['V', 'A1'], - 'A2/V': ['V', 'A2'], - 'AA/V': ['V', 'A1', 'A2']} +channel_map = { + 'A': ['A1'], + 'AA': ['A1', 'A2'], + 'B': ['V', 'A1'], + 'A2/V': ['V', 'A2'], + 'AA/V': ['V', 'A1', 'A2'] +} class EDLParser(object): - def __init__(self, edl_string): self.timeline = otio.schema.Timeline() @@ -56,8 +83,10 @@ def add_clip(self, line, comments): if comment_handler.unhandled: clip_handler.clip.metadata.setdefault("cmx_3600", {}) clip_handler.clip.metadata['cmx_3600'].setdefault("comments", []) - clip_handler.clip.metadata['cmx_3600'][ - 'comments'] += comment_handler.unhandled + clip_handler.clip.metadata['cmx_3600']['comments'] += ( + comment_handler.unhandled + ) + # each edit point between two clips is a transition. the default is a # cut in the edl format the transition codes are for the transition # into the clip @@ -121,7 +150,7 @@ def parse_edl(self, edl_string): if not line: continue - elif line.startswith('TITLE:'): + if line.startswith('TITLE:'): # this is the first line of interest in an edl # it is required to be in the header self.timeline.name = line.replace('TITLE:', '').strip() @@ -145,10 +174,12 @@ def parse_edl(self, edl_string): video_delay = line.split()[-1].strip() if audio_delay and video_delay: raise RuntimeError( - 'both audio and video delay declared after SPLIT.') + 'both audio and video delay declared after SPLIT.' + ) if not (audio_delay or video_delay): raise RuntimeError( - 'either audio or video delay declared after SPLIT.') + 'either audio or video delay declared after SPLIT.' + ) line_1 = edl_lines.pop(0) line_2 = edl_lines.pop(0) @@ -243,7 +274,8 @@ def make_clip(self, comment_data): # determine this for sure... m = re.match( r'(\d\d:\d\d:\d\d:\d\d)\s+(\w*)\s+(.*)', - comment_data["locator"]) + comment_data["locator"] + ) if m: marker = otio.schema.Marker() marker.marked_range = otio.opentime.TimeRange( @@ -296,27 +328,31 @@ def parse(self, line): # transition data for D and W*** transitions is a n integer that # denotes frame count # i haven't figured out how the key transitions (K, KB, KO) work - (self.clip_num, - self.reel, - self.channel_code, - self.transition_type, - self.transition_data, - self.source_tc_in, - self.source_tc_out, - self.record_tc_in, - self.record_tc_out) = fields + ( + self.clip_num, + self.reel, + self.channel_code, + self.transition_type, + self.transition_data, + self.source_tc_in, + self.source_tc_out, + self.record_tc_in, + self.record_tc_out + ) = fields elif field_count == 8: # no transition data # this is for basic cuts - (self.clip_num, - self.reel, - self.channel_code, - self.transition_type, - self.source_tc_in, - self.source_tc_out, - self.record_tc_in, - self.record_tc_out) = fields + ( + self.clip_num, + self.reel, + self.channel_code, + self.transition_type, + self.source_tc_in, + self.source_tc_out, + self.record_tc_in, + self.record_tc_out + ) = fields else: raise RuntimeError( @@ -354,8 +390,7 @@ def parse(self, comment): def expand_transitions(timeline): - """ Convert clips with metadata/transition == 'D' into OTIO transitions. - """ + """Convert clips with metadata/transition == 'D' into OTIO transitions.""" tracks = timeline.tracks remove_list = [] @@ -545,7 +580,9 @@ def write_to_string(input_otio): source_tc_in, source_tc_out, record_tc_in, - record_tc_out)) + record_tc_out + ) + ) if name: # Avid Media Composer outputs two spaces before the diff --git a/opentimelineio/adapters/fcp_xml.py b/opentimelineio/adapters/fcp_xml.py index 57b99f7f1..19dac3f86 100644 --- a/opentimelineio/adapters/fcp_xml.py +++ b/opentimelineio/adapters/fcp_xml.py @@ -1,3 +1,29 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""OpenTimelineIO Final Cut Pro 7 XML Adapter.""" + import os import math import functools @@ -56,17 +82,19 @@ def _populate_backreference_map(item, br_map): # skip unspecified tags if tag is not None: - br_map[tag].setdefault(item_hash, - 1 if not br_map[tag] else - max(br_map[tag].values()) + 1) + br_map[tag].setdefault( + item_hash, + 1 if not br_map[tag] else max(br_map[tag].values()) + 1 + ) # populate children if isinstance(item, otio.schema.Timeline): for sub_item in item.tracks: _populate_backreference_map(sub_item, br_map) - elif isinstance(item, (otio.schema.Clip, - otio.schema.Gap, - otio.schema.Transition)): + elif isinstance( + item, + (otio.schema.Clip, otio.schema.Gap, otio.schema.Transition) + ): pass else: for sub_item in item: @@ -91,11 +119,14 @@ def wrapper(item, *args, **kwargs): item_hash = item.__hash__() item_id = br_map[tag].get(item_hash, None) if item_id is not None: - return cElementTree.Element(tag, - id='{}-{}'.format(tag, item_id)) - item_id = br_map[tag].setdefault(item_hash, - 1 if not br_map[tag] else - max(br_map[tag].values()) + 1) + return cElementTree.Element( + tag, + id='{}-{}'.format(tag, item_id) + ) + item_id = br_map[tag].setdefault( + item_hash, + 1 if not br_map[tag] else max(br_map[tag].values()) + 1 + ) elem = func(item, *args, **kwargs) elem.attrib['id'] = '{}-{}'.format(tag, item_id) return elem @@ -203,15 +234,21 @@ def _parse_clip_item_without_media(clip_item, sequence_rate, transition_offsets[1].rescaled_to(rate) ] - in_frame = int(clip_item.find('./in').text) + \ - int(round(context_transition_offsets[0].value)) - out_frame = int(clip_item.find('./out').text) - \ - int(round(context_transition_offsets[1].value)) + in_frame = ( + int(clip_item.find('./in').text) + + int(round(context_transition_offsets[0].value)) + ) + out_frame = ( + int(clip_item.find('./out').text) + - int(round(context_transition_offsets[1].value)) + ) source_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime(in_frame, sequence_rate), - duration=otio.opentime.RationalTime(out_frame - in_frame, - sequence_rate) + duration=otio.opentime.RationalTime( + out_frame - in_frame, + sequence_rate + ) ) name_item = clip_item.find('name') @@ -222,7 +259,8 @@ def _parse_clip_item_without_media(clip_item, sequence_rate, clip = otio.schema.Clip(name=name, source_range=source_range) clip.markers.extend( - [_parse_marker(m, rate) for m in markers]) + [_parse_marker(m, rate) for m in markers] + ) return clip @@ -242,10 +280,14 @@ def _parse_clip_item(clip_item, transition_offsets, element_map): transition_offsets[1].rescaled_to(src_rate) ] - in_frame = int(clip_item.find('./in').text) + \ - int(round(context_transition_offsets[0].value)) - out_frame = int(clip_item.find('./out').text) - \ - int(round(context_transition_offsets[1].value)) + in_frame = ( + int(clip_item.find('./in').text) + + int(round(context_transition_offsets[0].value)) + ) + out_frame = ( + int(clip_item.find('./out').text) + - int(round(context_transition_offsets[1].value)) + ) timecode = media_reference.available_range.start_time # source_start in xml is taken relative to the start of the media, whereas @@ -263,9 +305,11 @@ def _parse_clip_item(clip_item, transition_offsets, element_map): url_path = _url_to_path(media_reference.target_url) name = os.path.basename(url_path) - clip = otio.schema.Clip(name=name, - media_reference=media_reference, - source_range=source_range) + clip = otio.schema.Clip( + name=name, + media_reference=media_reference, + source_range=source_range + ) clip.markers.extend([_parse_marker(m, src_rate) for m in markers]) return clip @@ -275,9 +319,11 @@ def _parse_transition_item(transition_item, sequence_rate): start = int(transition_item.find('./start').text) end = int(transition_item.find('./end').text) cut_point = _get_transition_cut_point(transition_item) - metadata = {META_NAMESPACE: { - 'effectid': transition_item.find('./effect/effectid').text, - }} + metadata = { + META_NAMESPACE: { + 'effectid': transition_item.find('./effect/effectid').text, + } + } transition = otio.schema.Transition( name=transition_item.find('./effect/name').text, @@ -300,10 +346,14 @@ def _parse_sequence_item(sequence_item, transition_offsets, element_map): transition_offsets[1].rescaled_to(source_rate) ] - in_frame = int(sequence_item.find('./in').text) + \ - int(round(context_transition_offsets[0].value, 0)) - out_frame = int(sequence_item.find('./out').text) - \ - int(round(context_transition_offsets[1].value)) + in_frame = ( + int(sequence_item.find('./in').text) + + int(round(context_transition_offsets[0].value, 0)) + ) + out_frame = ( + int(sequence_item.find('./out').text) + - int(round(context_transition_offsets[1].value)) + ) sequence.source_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime(in_frame, source_rate), @@ -333,14 +383,18 @@ def _parse_item(track_item, sequence_rate, transition_offsets, element_map): return _parse_sequence_item( track_item, transition_offsets, element_map) - raise TypeError('Type of clip item is not supported {item_id}'.format( - item_id=track_item.attrib['id'])) + raise TypeError( + 'Type of clip item is not supported {item_id}'.format( + item_id=track_item.attrib['id'] + ) + ) def _parse_track(track_e, kind, rate, element_map): track = otio.schema.Sequence(kind=kind) - track_items = [item for item in track_e - if item.tag in ('clipitem', 'transitionitem')] + track_items = [ + item for item in track_e if item.tag in ('clipitem', 'transitionitem') + ] if not track_items: return track @@ -354,31 +408,40 @@ def _parse_track(track_e, kind, rate, element_map): # start time and end time on the timeline can be set to -1. This means # that there is a transition at that end of the clip-item. So the time # on the timeline has to be taken from that object. - transition_offsets = [otio.opentime.RationalTime(), - otio.opentime.RationalTime()] + transition_offsets = [ + otio.opentime.RationalTime(), + otio.opentime.RationalTime() + ] + if track_item.tag == 'clipitem': if start == -1: in_transition = list(track_e)[clip_item_index - 1] start = _get_transition_cut_point(in_transition) transition_offsets[0] = otio.opentime.RationalTime( - start - int(in_transition.find('./start').text), rate) + start - int(in_transition.find('./start').text), + rate + ) if end == -1: out_transition = list(track_e)[clip_item_index + 1] end = _get_transition_cut_point(out_transition) transition_offsets[1] = otio.opentime.RationalTime( - int(out_transition.find('./end').text) - end, rate) + int(out_transition.find('./end').text) - end, + rate + ) # see if we need to add a gap before this clip-item gap_time = start - last_clip_end last_clip_end = end if gap_time > 0: gap_range = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(gap_time, rate)) + duration=otio.opentime.RationalTime(gap_time, rate) + ) track.append(otio.schema.Gap(source_range=gap_range)) # finally add the track-item itself - track.append(_parse_item(track_item, rate, - transition_offsets, element_map)) + track.append( + _parse_item(track_item, rate, transition_offsets, element_map) + ) return track @@ -386,11 +449,15 @@ def _parse_track(track_e, kind, rate, element_map): def _parse_marker(marker, rate): marker_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime( - int(marker.find('./in').text), rate)) + int(marker.find('./in').text), rate + ) + ) metadata = {META_NAMESPACE: {'comment': marker.find('./comment').text}} - return otio.schema.Marker(name=marker.find('./name').text, - marked_range=marker_range, - metadata=metadata) + return otio.schema.Marker( + name=marker.find('./name').text, + marked_range=marker_range, + metadata=metadata + ) def _parse_sequence(sequence, element_map): @@ -405,17 +472,22 @@ def _parse_sequence(sequence, element_map): stack = otio.schema.Stack(name=sequence.find('./name').text) stack.extend( - [_parse_track( - t, otio.schema.SequenceKind.Video, sequence_rate, element_map - ) - for t in video_tracks]) + _parse_track( + t, + otio.schema.SequenceKind.Video, sequence_rate, element_map + ) + for t in video_tracks + ) stack.extend( - [_parse_track( - t, otio.schema.SequenceKind.Audio, sequence_rate, element_map - ) - for t in audio_tracks - if _is_primary_audio_channel(t)]) - stack.markers.extend([_parse_marker(m, sequence_rate) for m in markers]) + _parse_track( + t, + otio.schema.SequenceKind.Audio, + sequence_rate, element_map + ) + for t in audio_tracks + if _is_primary_audio_channel(t) + ) + stack.markers.extend(_parse_marker(m, sequence_rate) for m in markers) return stack @@ -444,13 +516,21 @@ def _build_rate(time): rate_e = cElementTree.Element('rate') _insert_new_sub_element(rate_e, 'timebase', text=str(int(rate))) - _insert_new_sub_element(rate_e, 'ntsc', - text='FALSE' if rate == time.rate else 'TRUE') + _insert_new_sub_element( + rate_e, + 'ntsc', + text='FALSE' if rate == time.rate else 'TRUE' + ) return rate_e -def _build_item_timings(item_e, item, timeline_range, transition_offsets, - timecode): +def _build_item_timings( + item_e, + item, + timeline_range, + transition_offsets, + timecode +): # source_start is absolute time taking into account the timecode of the # media. But xml regards the source in point from the start of the media. # So we subtract the media timecode. @@ -470,14 +550,18 @@ def _build_item_timings(item_e, item, timeline_range, transition_offsets, item_e, 'duration', text='{:.0f}'.format(item.source_range.duration.value) ) - _insert_new_sub_element(item_e, 'start', - text=start) - _insert_new_sub_element(item_e, 'end', - text=end) - _insert_new_sub_element(item_e, 'in', - text='{:.0f}'.format(source_start.value)) - _insert_new_sub_element(item_e, 'out', - text='{:.0f}'.format(source_end.value)) + _insert_new_sub_element(item_e, 'start', text=start) + _insert_new_sub_element(item_e, 'end', text=end) + _insert_new_sub_element( + item_e, + 'in', + text='{:.0f}'.format(source_start.value) + ) + _insert_new_sub_element( + item_e, + 'out', + text='{:.0f}'.format(source_end.value) + ) @_backreference_build('file') @@ -501,7 +585,8 @@ def _build_file(media_reference, br_map): file_e.append(_build_rate(available_range.start_time)) _insert_new_sub_element( file_e, 'duration', - text='{:.0f}'.format(available_range.duration.value)) + text='{:.0f}'.format(available_range.duration.value) + ) _insert_new_sub_element(file_e, 'pathurl', text=media_reference.target_url) # timecode @@ -515,11 +600,17 @@ def _build_file(media_reference, br_map): text=otio.opentime.to_timecode(timecode, rate=timecode.rate) ) _insert_new_sub_element( - timecode_e, 'frame', text='{:.0f}'.format(timecode.value) + timecode_e, + 'frame', + text='{:.0f}'.format(timecode.value) ) - display_format = 'DF' if (math.ceil(timecode.rate) == 30 - and math.ceil(timecode.rate) != timecode.rate) \ + display_format = ( + 'DF' if ( + math.ceil(timecode.rate) == 30 + and math.ceil(timecode.rate) != timecode.rate + ) else 'NDF' + ) _insert_new_sub_element(timecode_e, 'displayformat', text=display_format) # we need to flag the file reference with the content types, otherwise it @@ -536,15 +627,21 @@ def _build_file(media_reference, br_map): return file_e -def _build_transition_item(transition_item, timeline_range, transition_offsets, - br_map): +def _build_transition_item( + transition_item, + timeline_range, + transition_offsets, + br_map +): transition_e = cElementTree.Element('transitionitem') _insert_new_sub_element( - transition_e, 'start', + transition_e, + 'start', text='{:.0f}'.format(timeline_range.start_time.value) ) _insert_new_sub_element( - transition_e, 'end', + transition_e, + 'end', text='{:.0f}'.format(timeline_range.end_time_exclusive().value) ) @@ -559,7 +656,9 @@ def _build_transition_item(transition_item, timeline_range, transition_offsets, transition_e.append(_build_rate(timeline_range.start_time)) effectid = transition_item.metadata.get(META_NAMESPACE, {}).get( - 'effectid', 'Cross Dissolve') + 'effectid', + 'Cross Dissolve' + ) effect_e = _insert_new_sub_element(transition_e, 'effect') _insert_new_sub_element(effect_e, 'name', text=transition_item.name) @@ -583,8 +682,13 @@ def _build_clip_item_without_media(clip_item, timeline_range, clip_item_e.extend([_build_marker(m) for m in clip_item.markers]) timecode = otio.opentime.RationalTime(0, timeline_range.start_time.rate) - _build_item_timings(clip_item_e, clip_item, timeline_range, - transition_offsets, timecode) + _build_item_timings( + clip_item_e, + clip_item, + timeline_range, + transition_offsets, + timecode + ) return clip_item_e @@ -599,16 +703,28 @@ def _build_clip_item(clip_item, timeline_range, transition_offsets, br_map): url_path = _url_to_path(clip_item.media_reference.target_url) name = os.path.basename(url_path) - _insert_new_sub_element(clip_item_e, 'name', - text=name) + _insert_new_sub_element( + clip_item_e, + 'name', + text=name + ) clip_item_e.append(_build_file(clip_item.media_reference, br_map)) - clip_item_e.append(_build_rate( - clip_item.media_reference.available_range.start_time)) - clip_item_e.extend([_build_marker(m) for m in clip_item.markers]) - timecode = clip_item.media_reference.available_range.start_time + if clip_item.media_reference.available_range: + clip_item_e.append( + _build_rate(clip_item.media_reference.available_range.start_time) + ) + clip_item_e.extend(_build_marker(m) for m in clip_item.markers) + + if clip_item.media_reference.available_range: + timecode = clip_item.media_reference.available_range.start_time - _build_item_timings(clip_item_e, clip_item, timeline_range, - transition_offsets, timecode) + _build_item_timings( + clip_item_e, + clip_item, + timeline_range, + transition_offsets, + timecode + ) return clip_item_e @@ -616,8 +732,11 @@ def _build_clip_item(clip_item, timeline_range, transition_offsets, br_map): def _build_sequence_item(sequence, timeline_range, transition_offsets, br_map): clip_item_e = cElementTree.Element('clipitem', frameBlend='FALSE') - _insert_new_sub_element(clip_item_e, 'name', - text=os.path.basename(sequence.name)) + _insert_new_sub_element( + clip_item_e, + 'name', + text=os.path.basename(sequence.name) + ) sequence_e = _build_sequence(sequence, timeline_range, br_map) @@ -626,27 +745,50 @@ def _build_sequence_item(sequence, timeline_range, transition_offsets, br_map): clip_item_e.append(sequence_e) timecode = otio.opentime.RationalTime(0, timeline_range.start_time.rate) - _build_item_timings(clip_item_e, sequence, timeline_range, - transition_offsets, timecode) + _build_item_timings( + clip_item_e, + sequence, + timeline_range, + transition_offsets, + timecode + ) return clip_item_e def _build_item(item, timeline_range, transition_offsets, br_map): if isinstance(item, otio.schema.Transition): - return _build_transition_item(item, timeline_range, transition_offsets, - br_map) + return _build_transition_item( + item, + timeline_range, + transition_offsets, + br_map + ) elif isinstance(item, otio.schema.Clip): - if isinstance(item.media_reference, - otio.media_reference.MissingReference): - return _build_clip_item_without_media(item, timeline_range, - transition_offsets, br_map) + if isinstance( + item.media_reference, + otio.media_reference.MissingReference + ): + return _build_clip_item_without_media( + item, + timeline_range, + transition_offsets, + br_map + ) else: - return _build_clip_item(item, timeline_range, transition_offsets, - br_map) + return _build_clip_item( + item, + timeline_range, + transition_offsets, + br_map + ) elif isinstance(item, otio.schema.Stack): - return _build_sequence_item(item, timeline_range, transition_offsets, - br_map) + return _build_sequence_item( + item, + timeline_range, + transition_offsets, + br_map + ) else: raise ValueError('Unsupported item: ' + str(item)) diff --git a/opentimelineio/adapters/otio_json.py b/opentimelineio/adapters/otio_json.py index 460d6a1bc..66b8db290 100644 --- a/opentimelineio/adapters/otio_json.py +++ b/opentimelineio/adapters/otio_json.py @@ -1,4 +1,28 @@ -""" This adapter lets you read and write native .otio files """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""This adapter lets you read and write native .otio files""" from .. import ( core diff --git a/opentimelineio/algorithms/__init__.py b/opentimelineio/algorithms/__init__.py index 9b69df37e..887e0ebb6 100644 --- a/opentimelineio/algorithms/__init__.py +++ b/opentimelineio/algorithms/__init__.py @@ -1,4 +1,28 @@ -""" Algorithms for OTIO objects. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Algorithms for OTIO objects.""" # flake8: noqa from .sequence_algo import ( diff --git a/opentimelineio/algorithms/sequence_algo.py b/opentimelineio/algorithms/sequence_algo.py index 3e7326f40..accbb0f3c 100644 --- a/opentimelineio/algorithms/sequence_algo.py +++ b/opentimelineio/algorithms/sequence_algo.py @@ -1,4 +1,28 @@ -""" Algorithms for sequence objects. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Algorithms for sequence objects.""" import copy @@ -9,7 +33,7 @@ def sequence_with_expanded_transitions(in_seq): - """ Expands transitions such that neighboring clips are trimmed into + """Expands transitions such that neighboring clips are trimmed into regions of overlap. For example, if your sequence is: @@ -122,7 +146,8 @@ def _expand_transition(target_transition, from_sequence): post.source_range = copy.deepcopy(post.trimmed_range()) post.source_range.start_time = ( - post.source_range.start_time - target_transition.in_offset + post.source_range.start_time + - target_transition.in_offset ).rescaled_to(post.source_range.start_time) post.source_range.duration = trx_duration.rescaled_to( post.source_range.start_time @@ -140,15 +165,18 @@ def _trim_from_transitions(thing, pre=None, post=None): if pre: result.source_range.start_time = ( - result.source_range.start_time + pre.out_offset + result.source_range.start_time + + pre.out_offset ) result.source_range.duration = ( - result.source_range.duration - pre.out_offset + result.source_range.duration + - pre.out_offset ) if post: result.source_range.duration = ( - result.source_range.duration - post.in_offset + result.source_range.duration + - post.in_offset ) return result diff --git a/opentimelineio/core/__init__.py b/opentimelineio/core/__init__.py index f16f92ade..7a701a09c 100644 --- a/opentimelineio/core/__init__.py +++ b/opentimelineio/core/__init__.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Internal implementation details of OpenTimelineIO.""" # flake8: noqa diff --git a/opentimelineio/core/composable.py b/opentimelineio/core/composable.py index d6c89741d..9515aa08e 100644 --- a/opentimelineio/core/composable.py +++ b/opentimelineio/core/composable.py @@ -1,5 +1,30 @@ -""" -Composable class definition. An object that can be composed by sequences. +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Composable class definition. + +An object that can be composed by sequences. """ from . import serializeable_object diff --git a/opentimelineio/core/composition.py b/opentimelineio/core/composition.py index 9b9cd5f97..b34684992 100644 --- a/opentimelineio/core/composition.py +++ b/opentimelineio/core/composition.py @@ -1,4 +1,28 @@ -""" Composition base class. An object that contains `Items`. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Composition base class. An object that contains `Items`.""" import collections @@ -184,8 +208,9 @@ def __deepcopy__(self, md): def _path_to_child(self, child): if not isinstance(child, item.Item): raise TypeError( - "An object child of 'Item' is required, not type '{}'" - "".format(type(child)) + "An object child of 'Item' is required, not type '{}'".format( + type(child) + ) ) current = child @@ -196,8 +221,7 @@ def _path_to_child(self, child): current = current._parent except AttributeError: raise exceptions.NotAChildError( - "Item '{}' is not a child of '{}'." - "".format(child, self) + "Item '{}' is not a child of '{}'.".format(child, self) ) parents.append(current) @@ -205,8 +229,9 @@ def _path_to_child(self, child): return parents def range_of_child(self, child, reference_space=None): - """The range of the child in relation to another item (reference_space), - not trimmed based on this based on this composition's source_range. + """The range of the child in relation to another item + (reference_space), not trimmed based on this based on this + composition's source_range. Note that reference_space must be in the same timeline as self. @@ -243,8 +268,8 @@ def range_of_child(self, child, reference_space=None): continue result_range.start_time = ( - result_range.start_time + - parent_range.start_time + result_range.start_time + + parent_range.start_time ) result_range.duration = result_range.duration current = parent @@ -329,8 +354,8 @@ def trimmed_range_of_child(self, child, reference_space=None): continue result_range.start_time = ( - result_range.start_time + - parent_range.start_time + result_range.start_time + + parent_range.start_time ) result_range.duration = result_range.duration current = parent diff --git a/opentimelineio/core/item.py b/opentimelineio/core/item.py index f5fc70091..10a183a04 100644 --- a/opentimelineio/core/item.py +++ b/opentimelineio/core/item.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Implementation of the Item base class. OTIO Objects that contain media.""" from .. import ( diff --git a/opentimelineio/core/json_serializer.py b/opentimelineio/core/json_serializer.py index c900d549e..f712db826 100644 --- a/opentimelineio/core/json_serializer.py +++ b/opentimelineio/core/json_serializer.py @@ -1,4 +1,28 @@ -""" Serializer for SerializeableObjects to JSON +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Serializer for SerializeableObjects to JSON Used for the otio_json adapter as well as for plugins and manifests. """ @@ -32,8 +56,9 @@ def default(self, obj): def serialize_json_to_string(root, sort_keys=True, indent=4): - """ - Serialize a tree of SerializeableObject to JSON. Returns a JSON string. + """Serialize a tree of SerializeableObject to JSON. + + Returns a JSON string. """ return _SerializeableObjectEncoder( diff --git a/opentimelineio/core/serializeable_object.py b/opentimelineio/core/serializeable_object.py index e16aad142..af32fd57d 100644 --- a/opentimelineio/core/serializeable_object.py +++ b/opentimelineio/core/serializeable_object.py @@ -1,4 +1,28 @@ -""" Implements the otio.core.SerializeableObject """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Implements the otio.core.SerializeableObject""" import copy @@ -147,9 +171,9 @@ def getter(self): def setter(self, val): # always allow None values regardless of value of required_type if ( - required_type is not None and - val is not None and - not isinstance(val, required_type) + required_type is not None + and val is not None + and not isinstance(val, required_type) ): raise TypeError( "attribute '{}' must be an instance of '{}', not: {}".format( diff --git a/opentimelineio/core/type_registry.py b/opentimelineio/core/type_registry.py index e9b700fce..089cd64b6 100644 --- a/opentimelineio/core/type_registry.py +++ b/opentimelineio/core/type_registry.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Core type registry system for registering OTIO types for serialization.""" from .. import ( diff --git a/opentimelineio/exceptions.py b/opentimelineio/exceptions.py index 86b464b8a..c74ed81c5 100644 --- a/opentimelineio/exceptions.py +++ b/opentimelineio/exceptions.py @@ -1,4 +1,28 @@ -__doc__ = """ Exception classes for OpenTimelineIO """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Exception classes for OpenTimelineIO""" class OTIOError(Exception): diff --git a/opentimelineio/media_linker.py b/opentimelineio/media_linker.py index cb41fdda2..f0b2a1275 100644 --- a/opentimelineio/media_linker.py +++ b/opentimelineio/media_linker.py @@ -1,6 +1,29 @@ -""" -MediaLinker plugins fire after an adapter has read a file in oder to produce -MediaReferences that point at valid, site specific media. +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +""" MediaLinker plugins fire after an adapter has read a file in oder to +produce MediaReferences that point at valid, site specific media. They expose a "link_media_reference" function with the signature: link_media_reference :: otio.schema.Clip -> otio.media_reference.MediaReference diff --git a/opentimelineio/media_reference.py b/opentimelineio/media_reference.py index 1de7900ef..e7cfcf945 100644 --- a/opentimelineio/media_reference.py +++ b/opentimelineio/media_reference.py @@ -1,6 +1,28 @@ -""" -Media Reference Classes and Functions. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Media Reference Classes and Functions.""" from . import ( opentime, @@ -10,7 +32,6 @@ @core.register_type class MediaReference(core.SerializeableObject): - """Base Media Reference Class. Currently handles string printing the child classes, which expose interface @@ -38,7 +59,9 @@ def __init__( self.metadata = metadata name = core.serializeable_field( - "name", doc="Name of this media reference.") + "name", + doc="Name of this media reference." + ) available_range = core.serializeable_field( "available_range", opentime.TimeRange, diff --git a/opentimelineio/opentime.py b/opentimelineio/opentime.py index 9fd260735..61df4ec04 100644 --- a/opentimelineio/opentime.py +++ b/opentimelineio/opentime.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Library for expressing and transforming time. Defaults to 24 fps, but allows the caller to specify an override. @@ -11,9 +35,8 @@ class RationalTime(object): - """ - Represents an instantaneous point in time, value * (1/rate) seconds from - time 0seconds. + """ Represents an instantaneous point in time, value * (1/rate) seconds + from time 0seconds. """ def __init__(self, value=0, rate=1): @@ -21,7 +44,7 @@ def __init__(self, value=0, rate=1): self.rate = rate def rescaled_to(self, new_rate): - """ returns the time for this time converted to new_rate """ + """Returns the time for this time converted to new_rate""" if isinstance(new_rate, RationalTime): new_rate = new_rate.rate @@ -32,7 +55,7 @@ def rescaled_to(self, new_rate): ) def value_rescaled_to(self, new_rate): - """ returns the time value for self converted to new_rate """ + """Returns the time value for self converted to new_rate""" if new_rate == self.rate: return self.value @@ -82,7 +105,7 @@ def __iadd__(self, other): return self def __add__(self, other): - """ Returns a RationalTime object that is the sum of self and other. + """Returns a RationalTime object that is the sum of self and other. If self and other have differing time rates, the result will have the have the rate of the faster time. @@ -113,8 +136,7 @@ def __sub__(self, other): return RationalTime(value=value, rate=scale) def _comparable_floats(self, other): - """ - returns a tuple of two floats, (self, other), which are suitable + """Returns a tuple of two floats, (self, other), which are suitable for comparison. If other is not of a type that can be compared, TypeError is raised @@ -174,7 +196,7 @@ def __hash__(self): class TimeTransform(object): - """ 1D Transform for RationalTime. Has offset and scale. """ + """1D Transform for RationalTime. Has offset and scale.""" def __init__(self, offset=RationalTime(), scale=1.0, rate=None): self.offset = offset @@ -244,14 +266,14 @@ def __hash__(self): class BoundStrategy(object): - """ Different bounding strategies for TimeRange """ + """Different bounding strategies for TimeRange """ Free = 1 Clamp = 2 class TimeRange(object): - """ Contains a range of time, starting (and including) start_time and + """Contains a range of time, starting (and including) start_time and lasting duration.value * (1/duration.rate) seconds. A 0 duration TimeRange is the same as a RationalTime, and contains only the @@ -276,8 +298,7 @@ def duration(self, val): self._duration = val def end_time_inclusive(self): - """ - The time of the last sample that contains data in the TimeRange. + """The time of the last sample that contains data in the TimeRange. If the TimeRange goes from (0, 24) w/ duration (10, 24), this will be (9, 24) @@ -307,8 +328,7 @@ def end_time_inclusive(self): return self.start_time def end_time_exclusive(self): - """" - Time of the first sample outside the time range. + """"Time of the first sample outside the time range. If Start Frame is 10 and duration is 5, then end_time_exclusive is 15, even though the last time with data in this range is 14. @@ -320,7 +340,7 @@ def end_time_exclusive(self): return self.duration + self.start_time.rescaled_to(self.duration) def extended_by(self, other): - """ Construct a new TimeRange that is this one extended by another. """ + """Construct a new TimeRange that is this one extended by another.""" result = TimeRange(self.start_time, self.duration) if isinstance(other, TimeRange): @@ -348,10 +368,9 @@ def clamped( start_bound=BoundStrategy.Free, end_bound=BoundStrategy.Free ): - """ - Apply the range to either a RationalTime or a TimeRange. If applied to - a TimeRange, the resulting TimeRange will have the same boundary policy - as other. (in other words, _not_ the same as self). + """Apply the range to either a RationalTime or a TimeRange. If + applied to a TimeRange, the resulting TimeRange will have the same + boundary policy as other. (in other words, _not_ the same as self). """ if isinstance(other, RationalTime): @@ -383,8 +402,8 @@ def clamped( return self def contains(self, other): - """ - Return true if self completely contains other. + """Return true if self completely contains other. + (RationalTime or TimeRange) """ @@ -404,8 +423,8 @@ def contains(self, other): ) def overlaps(self, other): - """ - Return true if self overlaps any part of other. + """Return true if self overlaps any part of other. + (RationalTime or TimeRange) """ @@ -456,13 +475,13 @@ def __str__(self): def from_frames(frame, fps): - """ - Turn a frame number and fps into a time object. + """Turn a frame number and fps into a time object. For any integer fps value, the rate will be the fps. For any common non-integer fps value (e.g. 29.97, 23.98) the time scale will be 600. """ + if int(fps) == fps: return RationalTime(frame, int(fps)) elif int(fps * 600) == fps * 600: @@ -482,8 +501,7 @@ def to_frames(time_obj, fps=None): def from_timecode(timecode_str, rate=24.0): - """ - Convert a timecode string into a RationalTime. + """Convert a timecode string into a RationalTime. :param timecode_str: (:class:`str`) A colon-delimited timecode. :param rate: (:class:`float`) The frame-rate to calculate timecode in @@ -491,6 +509,7 @@ def from_timecode(timecode_str, rate=24.0): :return: (:class:`RationalTime`) Instance for the timecode provided. """ + if ';' in timecode_str: raise ValueError('Drop-Frame timecodes not supported.') @@ -514,8 +533,7 @@ def from_timecode(timecode_str, rate=24.0): def to_timecode(time_obj, rate): - """ - Convert a RationalTime into a timecode string. + """Convert a RationalTime into a timecode string. :param time_obj: (:class:`RationalTime`) instance to express as timecode. :param rate: (:class:`float`) The frame-rate to calculate timecode in @@ -553,7 +571,8 @@ def to_timecode(time_obj, rate): def from_seconds(seconds): - """ Convert a number of seconds into RationalTime """ + """Convert a number of seconds into RationalTime""" + # Note: in the future we may consider adding a preferred rate arg time_obj = RationalTime(value=seconds, rate=1) @@ -574,8 +593,7 @@ def to_footage(time_obj): def duration_from_start_end_time(start_time, end_time_exclusive): - """ - Compute duration of samples from first to last. This is not the same as + """Compute duration of samples from first to last. This is not the same as distance. For example, the duration of a clip from frame 10 to frame 15 is 6 frames. Result in the rate of start_time. """ diff --git a/opentimelineio/plugins/__init__.py b/opentimelineio/plugins/__init__.py index 866122e55..dedb3da37 100644 --- a/opentimelineio/plugins/__init__.py +++ b/opentimelineio/plugins/__init__.py @@ -1,8 +1,32 @@ -""" Plugin system for OTIO """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Plugin system for OTIO""" # flake8: noqa -from .python_plugin import PythonPlugin +from .python_plugin import PythonPlugin from .manifest import ( manifest_from_file, ActiveManifest, diff --git a/opentimelineio/plugins/manifest.py b/opentimelineio/plugins/manifest.py index f6bbead8d..ddf891f91 100644 --- a/opentimelineio/plugins/manifest.py +++ b/opentimelineio/plugins/manifest.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Implementation of an adapter registry system for OTIO.""" import os diff --git a/opentimelineio/plugins/python_plugin.py b/opentimelineio/plugins/python_plugin.py index 496e463fd..ee72ede6d 100644 --- a/opentimelineio/plugins/python_plugin.py +++ b/opentimelineio/plugins/python_plugin.py @@ -1,4 +1,28 @@ -""" Base class for OTIO plugins that are exposed by manifests. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Base class for OTIO plugins that are exposed by manifests.""" import os import imp @@ -10,7 +34,7 @@ class PythonPlugin(core.SerializeableObject): - """ A class of plugin that is encoded in a python module, exposed via a + """A class of plugin that is encoded in a python module, exposed via a manifest. """ diff --git a/opentimelineio/schema/__init__.py b/opentimelineio/schema/__init__.py index 29fc78e6c..9bc360a73 100644 --- a/opentimelineio/schema/__init__.py +++ b/opentimelineio/schema/__init__.py @@ -1,5 +1,31 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + # flake8: noqa +"""User facing classes.""" + from .clip import ( Clip, ) diff --git a/opentimelineio/schema/clip.py b/opentimelineio/schema/clip.py index f89f22ed9..9545019652 100644 --- a/opentimelineio/schema/clip.py +++ b/opentimelineio/schema/clip.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Implementation of the Clip class, for pointing at media.""" from .. import ( diff --git a/opentimelineio/schema/effect.py b/opentimelineio/schema/effect.py index 0bcbe847a..4f9d76e4f 100644 --- a/opentimelineio/schema/effect.py +++ b/opentimelineio/schema/effect.py @@ -1,6 +1,28 @@ -""" -Implementation of Effect OTIO class. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Implementation of Effect OTIO class.""" from .. import ( core diff --git a/opentimelineio/schema/gap.py b/opentimelineio/schema/gap.py index a2d9afa81..31726044d 100755 --- a/opentimelineio/schema/gap.py +++ b/opentimelineio/schema/gap.py @@ -1,8 +1,30 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# from .. import core -__doc__ = """Gap Item - represents a transparent gap in content.""" +"""Gap Item - represents a transparent gap in content.""" @core.register_type diff --git a/opentimelineio/schema/marker.py b/opentimelineio/schema/marker.py index d64b8e5f0..2f5147e82 100644 --- a/opentimelineio/schema/marker.py +++ b/opentimelineio/schema/marker.py @@ -1,4 +1,28 @@ -""" Marker class. Holds metadata over regions of time. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Marker class. Holds metadata over regions of time.""" from .. import ( core, diff --git a/opentimelineio/schema/sequence.py b/opentimelineio/schema/sequence.py index c392379c3..820cf06ab 100644 --- a/opentimelineio/schema/sequence.py +++ b/opentimelineio/schema/sequence.py @@ -1,4 +1,28 @@ -""" Imeplement Sequence sublcass of composition. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Imeplement Sequence sublcass of composition.""" from .. import ( core, @@ -56,9 +80,9 @@ def range_of_child_at_index(self, index): # sum the durations of all the children leading up to the chosen one start_time = sum( - map( - lambda current_item: current_item.duration(), - (i for i in self[:index] if not i.overlapping()) + ( + o_c.duration() + for o_c in (c for c in self[:index] if not c.overlapping()) ), opentime.RationalTime(value=0, rate=child.duration().rate) ) @@ -75,8 +99,8 @@ def trimmed_range_of_child_at_index(self, index, reference_space=None): # cropped out entirely if ( - self.source_range.start_time >= range.end_time_exclusive() or - self.source_range.end_time_exclusive() <= range.start_time + self.source_range.start_time >= range.end_time_exclusive() + or self.source_range.end_time_exclusive() <= range.start_time ): return None diff --git a/opentimelineio/schema/serializeable_collection.py b/opentimelineio/schema/serializeable_collection.py index a18f05c5d..2258a99ff 100644 --- a/opentimelineio/schema/serializeable_collection.py +++ b/opentimelineio/schema/serializeable_collection.py @@ -1,6 +1,28 @@ -""" -A serializeable collection of SerializeableObjects. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""A serializeable collection of SerializeableObjects.""" import collections @@ -14,8 +36,7 @@ class SerializeableCollection( core.SerializeableObject, collections.MutableSequence ): - """ - A special kind of composition which can hold any serializeable object. + """A kind of composition which can hold any serializeable object. This composition approximates the concept of a `bin` - a collection of SerializeableObjects that do not have any compositing meaning, but can diff --git a/opentimelineio/schema/stack.py b/opentimelineio/schema/stack.py index 4e7edae00..cb48bc762 100644 --- a/opentimelineio/schema/stack.py +++ b/opentimelineio/schema/stack.py @@ -1,6 +1,28 @@ -""" -Implement Sequence and Stack. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Implement Sequence and Stack.""" from .. import ( core, diff --git a/opentimelineio/schema/timeline.py b/opentimelineio/schema/timeline.py index 3617eef8a..f9953f3c6 100644 --- a/opentimelineio/schema/timeline.py +++ b/opentimelineio/schema/timeline.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + """Implementation of the OTIO built in schema, Timeline object.""" from .. import ( diff --git a/opentimelineio/schema/transition.py b/opentimelineio/schema/transition.py index 9121e7a7e..a555de209 100644 --- a/opentimelineio/schema/transition.py +++ b/opentimelineio/schema/transition.py @@ -1,4 +1,28 @@ -""" Transition base class """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Transition base class""" from .. import ( opentime, @@ -7,7 +31,7 @@ class TransitionTypes: - """ Enum encoding types of transitions. + """Enum encoding types of transitions. This is for representing "Dissolves" and "Wipes" defined by the multi-source effect as defined by: @@ -27,7 +51,7 @@ class TransitionTypes: @core.register_type class Transition(core.Composable): - """ Represents a transition between two items. """ + """Represents a transition between two items.""" _serializeable_label = "Transition.1" diff --git a/opentimelineioViewWidget/__init__.py b/opentimelineioViewWidget/__init__.py deleted file mode 100644 index 781b74273..000000000 --- a/opentimelineioViewWidget/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import details -from . import timeline \ No newline at end of file diff --git a/opentimelineioViewWidget/details.py b/opentimelineioViewWidget/details.py deleted file mode 100644 index 704ad606f..000000000 --- a/opentimelineioViewWidget/details.py +++ /dev/null @@ -1,14 +0,0 @@ -from PySide import QtGui - - -class Details(QtGui.QTextEdit): - - def __init__(self, *args, **kwargs): - super(Details, self).__init__(*args, **kwargs) - self.setFixedHeight(100) - - def set_item(self, item): - if item is None: - self.setPlainText('') - else: - self.setPlainText(str(item)) diff --git a/opentimelineview/__init__.py b/opentimelineview/__init__.py new file mode 100644 index 000000000..9926045cb --- /dev/null +++ b/opentimelineview/__init__.py @@ -0,0 +1,28 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +# flake8: noqa + +from . import details_widget +from . import timeline_widget diff --git a/opentimelineview/details_widget.py b/opentimelineview/details_widget.py new file mode 100644 index 000000000..34b4e168a --- /dev/null +++ b/opentimelineview/details_widget.py @@ -0,0 +1,42 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +from PySide import QtGui + +import opentimelineio as otio + + +class Details(QtGui.QTextEdit): + """Widget with the json string of the specified OTIO object.""" + + def __init__(self, *args, **kwargs): + super(Details, self).__init__(*args, **kwargs) + self.setFixedHeight(100) + + def set_item(self, item): + if item is None: + self.setPlainText('') + else: + s = otio.adapters.write_to_string(item, 'otio_json') + self.setPlainText(s) diff --git a/opentimelineioViewWidget/timeline.py b/opentimelineview/timeline_widget.py similarity index 70% rename from opentimelineioViewWidget/timeline.py rename to opentimelineview/timeline_widget.py index 95089b6d1..810184132 100644 --- a/opentimelineioViewWidget/timeline.py +++ b/opentimelineview/timeline_widget.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + from PySide import QtGui from PySide import QtCore @@ -12,9 +36,9 @@ MARKER_SIZE = 10 -class BaseItem(QtGui.QGraphicsRectItem): +class _BaseItem(QtGui.QGraphicsRectItem): def __init__(self, item, timeline_range, *args, **kwargs): - super(BaseItem, self).__init__(*args, **kwargs) + super(_BaseItem, self).__init__(*args, **kwargs) self.item = item self.timeline_range = timeline_range @@ -32,40 +56,51 @@ def __init__(self, item, timeline_range, *args, **kwargs): def paint(self, *args, **kwargs): new_args = [args[0], QtGui.QStyleOptionGraphicsItem()] + list(args[2:]) - super(BaseItem, self).paint(*new_args, **kwargs) + super(_BaseItem, self).paint(*new_args, **kwargs) def itemChange(self, change, value): if change == QtGui.QGraphicsItem.ItemSelectedHasChanged: - self.setPen(QtGui.QColor(0, 255, 0, 255) if self.isSelected() - else QtGui.QColor(0, 0, 0, 255)) - self.setZValue(self.zValue() + 1 - if self.isSelected() - else self.zValue() - 1) - return super(BaseItem, self).itemChange(change, value) + self.setPen( + QtGui.QColor(0, 255, 0, 255) if self.isSelected() + else QtGui.QColor(0, 0, 0, 255) + ) + self.setZValue( + self.zValue() + 1 if self.isSelected() + else self.zValue() - 1 + ) + + return super(_BaseItem, self).itemChange(change, value) def _add_markers(self): - source_range = (self.item.source_range.start_time, - self.item.source_range.end_time_exclusive()) + source_range = ( + self.item.source_range.start_time, + self.item.source_range.end_time_exclusive() + ) for m in self.item.markers: marked_time = m.marked_range.start_time if marked_time < source_range[0] or marked_time > source_range[1]: continue + + # @TODO: set the marker color if its set from the OTIO object marker = Marker(m, None, None) marker.setY(0.5 * MARKER_SIZE) marker.setX( - (otio.opentime.to_seconds(m.marked_range.start_time) - - otio.opentime.to_seconds(source_range[0])) * TIME_MULTIPLIER + ( + otio.opentime.to_seconds(m.marked_range.start_time) + - otio.opentime.to_seconds(source_range[0]) + ) * TIME_MULTIPLIER ) marker.setParentItem(self) def _position_labels(self): self.source_in_label.setY(LABEL_MARGIN) self.source_out_label.setY(LABEL_MARGIN) - self.source_name_label.setY((TRACK_HEIGHT - - LABEL_MARGIN - - self.source_name_label - .boundingRect().height())) + self.source_name_label.setY( + TRACK_HEIGHT + - LABEL_MARGIN + - self.source_name_label.boundingRect().height() + ) def _set_labels_rational_time(self): source_range = self.item.source_range @@ -113,8 +148,11 @@ def _set_labels(self): self._position_labels() def counteract_zoom(self, zoom_level=1.0): - for label in (self.source_name_label, self.source_in_label, - self.source_out_label): + for label in ( + self.source_name_label, + self.source_in_label, + self.source_out_label + ): label.setTransform(QtGui.QTransform.fromScale(zoom_level, 1.0)) self_rect = self.boundingRect() @@ -133,19 +171,19 @@ def counteract_zoom(self, zoom_level=1.0): self.source_in_label.setX(LABEL_MARGIN * zoom_level) - self.source_out_label.setX(self_rect.width() - - LABEL_MARGIN * zoom_level - - out_width) + self.source_out_label.setX( + self_rect.width() - LABEL_MARGIN * zoom_level - out_width + ) - if name_width + frames_space + LABEL_MARGIN * zoom_level > \ - self_rect.width(): + total_width = (name_width + frames_space + LABEL_MARGIN * zoom_level) + if total_width > self_rect.width(): self.source_name_label.setVisible(False) else: self.source_name_label.setVisible(True) self.source_name_label.setX(0.5 * (self_rect.width() - name_width)) -class GapItem(BaseItem): +class GapItem(_BaseItem): def __init__(self, *args, **kwargs): super(GapItem, self).__init__(*args, **kwargs) self.setBrush( @@ -154,7 +192,7 @@ def __init__(self, *args, **kwargs): self.source_name_label.setText('GAP') -class TransitionItem(BaseItem): +class TransitionItem(_BaseItem): def __init__(self, item, timeline_range, rect, *args, **kwargs): rect.setHeight(TRANSITION_HEIGHT) super(TransitionItem, self).__init__( @@ -173,10 +211,12 @@ def __init__(self, item, timeline_range, rect, *args, **kwargs): shading_poly_f.append(QtCore.QPointF(0, rect.height())) shading_poly = QtGui.QGraphicsPolygonItem(shading_poly_f, parent=self) - shading_poly.setBrush( - QtGui.QBrush(QtGui.QColor(0, 0, 0, 30)) - ) - shading_poly.setPen(QtCore.Qt.NoPen) + shading_poly.setBrush(QtGui.QBrush(QtGui.QColor(0, 0, 0, 30))) + + try: + shading_poly.setPen(QtCore.Qt.NoPen) + except TypeError: + shading_poly.setPen(QtCore.Qt.transparent) def _add_markers(self): return @@ -185,7 +225,7 @@ def _set_labels(self): return -class ClipItem(BaseItem): +class ClipItem(_BaseItem): def __init__(self, *args, **kwargs): super(ClipItem, self).__init__(*args, **kwargs) self.setBrush( @@ -194,7 +234,7 @@ def __init__(self, *args, **kwargs): self.source_name_label.setText(self.item.name) -class NestedItem(BaseItem): +class NestedItem(_BaseItem): def __init__(self, *args, **kwargs): super(NestedItem, self).__init__(*args, **kwargs) self.setBrush( @@ -204,7 +244,7 @@ def __init__(self, *args, **kwargs): self.source_name_label.setText(self.item.name) def mouseDoubleClickEvent(self, event): - super(BaseItem, self).mouseDoubleClickEvent(event) + super(_BaseItem, self).mouseDoubleClickEvent(event) self.scene().views()[0].open_stack.emit(self.item) @@ -226,7 +266,9 @@ def _populate(self): 0, otio.opentime.to_seconds(timeline_range.duration) * TIME_MULTIPLIER, - TRACK_HEIGHT) + TRACK_HEIGHT + ) + if isinstance(item, otio.schema.Clip): new_item = ClipItem(item, timeline_range, rect) elif isinstance(item, otio.schema.Stack): @@ -300,26 +342,39 @@ def _adjust_scene_size(self): all_ranges = [t.range_of_child_at_index(n) for t in self.stack for n in range(len(t))] - start_time = min(map(lambda child: child.start_time, all_ranges)) - end_time_exclusive = max(map(lambda child: child.end_time_exclusive(), all_ranges)) + if all_ranges: + start_time = min(c.start_time for c in all_ranges) + end_time_exclusive = max( + c.end_time_exclusive() for c in all_ranges + ) + else: + start_time = otio.opentime.RationalTime() + end_time_exclusive = otio.opentime.RationalTime() start_time = otio.opentime.to_seconds(start_time) duration = otio.opentime.to_seconds(end_time_exclusive) - has_video_tracks = any(t for t in self.stack - if t.kind == otio.schema.SequenceKind.Video) - has_audio_tracks = any(t for t in self.stack - if t.kind == otio.schema.SequenceKind.Audio) + has_video_tracks = any( + t for t in self.stack if t.kind == otio.schema.SequenceKind.Video + ) + has_audio_tracks = any( + t for t in self.stack if t.kind == otio.schema.SequenceKind.Audio + ) - height = TIME_SLIDER_HEIGHT + \ - int(has_video_tracks and has_audio_tracks) * \ - MEDIA_TYPE_SEPARATOR_HEIGHT + \ - len(self.stack) * TRACK_HEIGHT + height = ( + TIME_SLIDER_HEIGHT + + int( + has_video_tracks and has_audio_tracks + ) * MEDIA_TYPE_SEPARATOR_HEIGHT + + len(self.stack) * TRACK_HEIGHT + ) - self.setSceneRect(start_time * TIME_MULTIPLIER, - 0, - duration * TIME_MULTIPLIER, - height) + self.setSceneRect( + start_time * TIME_MULTIPLIER, + 0, + duration * TIME_MULTIPLIER, + height + ) def _add_time_slider(self): scene_rect = self.sceneRect() @@ -335,19 +390,24 @@ def _add_track(self, track, y_pos): new_track.setPos(scene_rect.x(), y_pos) def _add_tracks(self): - video_tracks = [t for t in self.stack - if t.kind == otio.schema.SequenceKind.Video - and list(t)] - audio_tracks = [t for t in self.stack - if t.kind == otio.schema.SequenceKind.Audio - and list(t)] + video_tracks = [ + t for t in self.stack + if t.kind == otio.schema.SequenceKind.Video and list(t) + ] + audio_tracks = [ + t for t in self.stack + if t.kind == otio.schema.SequenceKind.Audio and list(t) + ] video_tracks.reverse() video_tracks_top = TIME_SLIDER_HEIGHT - audio_tracks_top = TIME_SLIDER_HEIGHT + \ - len(video_tracks) * TRACK_HEIGHT + \ - int(bool(video_tracks) and bool(audio_tracks)) * \ - MEDIA_TYPE_SEPARATOR_HEIGHT + audio_tracks_top = ( + TIME_SLIDER_HEIGHT + + len(video_tracks) * TRACK_HEIGHT + + int( + bool(video_tracks) and bool(audio_tracks) + ) * MEDIA_TYPE_SEPARATOR_HEIGHT + ) for i, track in enumerate(audio_tracks): self._add_track(track, audio_tracks_top + i * TRACK_HEIGHT) @@ -358,8 +418,10 @@ def _add_tracks(self): def _add_markers(self): for m in self.stack.markers: marker = Marker(m, None, self) - marker.setX(otio.opentime.to_seconds( - m.marked_range.start_time) * TIME_MULTIPLIER) + marker.setX( + otio.opentime.to_seconds(m.marked_range.start_time) + * TIME_MULTIPLIER + ) marker.setY(TIME_SLIDER_HEIGHT - MARKER_SIZE) self.addItem(marker) @@ -385,9 +447,11 @@ def parse_selection_change(self): def mousePressEvent(self, mouse_event): modifiers = QtGui.QApplication.keyboardModifiers() - self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag - if modifiers == QtCore.Qt.AltModifier - else QtGui.QGraphicsView.NoDrag) + self.setDragMode( + QtGui.QGraphicsView.ScrollHandDrag + if modifiers == QtCore.Qt.AltModifier + else QtGui.QGraphicsView.NoDrag + ) self.setInteractive(not modifiers == QtCore.Qt.AltModifier) super(StackView, self).mousePressEvent(mouse_event) @@ -402,9 +466,10 @@ def wheelEvent(self, event): # some items we do want to keep the same visual size. So we need to # inverse the effect of the zoom - items_to_scale = [i for i in self.scene().items() - if isinstance(i, BaseItem) - or isinstance(i, Marker)] + items_to_scale = [ + i for i in self.scene().items() + if isinstance(i, _BaseItem) or isinstance(i, Marker) + ] for item in items_to_scale: item.counteract_zoom(zoom_level) @@ -436,10 +501,18 @@ def set_timeline(self, timeline): self.add_stack(timeline.tracks) def add_stack(self, stack): - tab_index = next((i for i in range(self.count()) - if stack == self.widget(i).scene().stack), -1) + """open a tab for the stack or go to it if already present""" + + # find the tab for the stack if the tab has already been opened + tab_index = next( + ( + i for i in range(self.count()) + if stack is self.widget(i).scene().stack + ), + None + ) - if tab_index >= 0: + if tab_index is not None: self.setCurrentIndex(tab_index) return @@ -448,7 +521,9 @@ def add_stack(self, stack): # cannot close the first tab if self.count() == 1: - self.tabBar().tabButton(0, QtGui.QTabBar.RightSide).resize(0, 0) + button = self.tabBar().tabButton(0, QtGui.QTabBar.RightSide) + if button: + button.resize(0, 0) new_stack.open_stack.connect(self.add_stack) new_stack.selection_changed.connect(self.selection_changed) diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index d9e60357f..1d65d7630 --- a/setup.py +++ b/setup.py @@ -1,19 +1,42 @@ #!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# -import sys +""" Configuration file for the OpenTimelineIO Python Package. """ -from distutils.core import setup +import sys +from setuptools import setup -""" Configuration file for the OpenTimelineIO Python Package. """ # check the python version first if ( sys.version_info[0] < 2 or - (sys.version_info[0] == 2 and sys.version_info[1] < 6) + (sys.version_info[0] == 2 and sys.version_info[1] < 7) ): sys.exit( - 'OpenTimelineIO requires python2.6 or greater, detected version:' + 'OpenTimelineIO requires python2.7 or greater, detected version:' ' {}.{}'.format( sys.version_info[0], sys.version_info[1] @@ -35,7 +58,7 @@ 'opentimelineio.core', 'opentimelineio.schema', 'opentimelineio.plugins', - 'opentimelineioViewWidget' + 'opentimelineview' ], package_data={ diff --git a/tests/baseline/example.py b/tests/baseline/example.py deleted file mode 100644 index f4703c1fc..000000000 --- a/tests/baseline/example.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -This file is here to support the test_adapter_plugin unittest. -If you want to learn how to write your own adapter plugin, please read: -https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter -""" - -import opentimelineio as otio - - -def read_from_file(filepath): - fake_tl = otio.schema.Timeline(name=filepath) - fake_tl.tracks.append(otio.schema.Sequence()) - fake_tl.tracks[0].append(otio.schema.Clip(name=filepath + "_clip")) - return fake_tl - - -def read_from_string(input_str): - return read_from_file(input_str) - - -# in practice, these will be in separate plugins, but for simplicity in the -# unit tests, we put this in the same file as the example adapter. -def link_media_reference(in_clip, media_linker_argument_map): - d = {'from_test_linker': True} - d.update(media_linker_argument_map) - return otio.media_reference.MissingReference( - name=in_clip.name + "_tweaked", - metadata=d - ) diff --git a/tests/baseline_reader.py b/tests/baseline_reader.py old mode 100644 new mode 100755 index 91a213370..670981cc4 --- a/tests/baseline_reader.py +++ b/tests/baseline_reader.py @@ -1,5 +1,28 @@ -#!/usr/bin/env python -""" Utilities for reading baseline json. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Utilities for reading baseline json.""" import os import json @@ -12,7 +35,7 @@ def test_hook(dct): return json_from_file_as_string( os.path.join( MODPATH, - "baseline", + "baselines", str(dct["FROM_TEST_FILE"]) ) ) @@ -30,7 +53,7 @@ def json_from_file_as_string(fpath): def path_to_baseline(name): - return os.path.join(MODPATH, "baseline", "{0}.json".format(name)) + return os.path.join(MODPATH, "baselines", "{0}.json".format(name)) def json_baseline(name): diff --git a/tests/baseline/adapter_example.json b/tests/baselines/adapter_example.json similarity index 100% rename from tests/baseline/adapter_example.json rename to tests/baselines/adapter_example.json diff --git a/tests/baseline/adapter_plugin_manifest.plugin_manifest.json b/tests/baselines/adapter_plugin_manifest.plugin_manifest.json similarity index 100% rename from tests/baseline/adapter_plugin_manifest.plugin_manifest.json rename to tests/baselines/adapter_plugin_manifest.plugin_manifest.json diff --git a/tests/baseline/empty_clip.json b/tests/baselines/empty_clip.json similarity index 100% rename from tests/baseline/empty_clip.json rename to tests/baselines/empty_clip.json diff --git a/tests/baseline/empty_external_reference.json b/tests/baselines/empty_external_reference.json similarity index 100% rename from tests/baseline/empty_external_reference.json rename to tests/baselines/empty_external_reference.json diff --git a/tests/baseline/empty_gap.json b/tests/baselines/empty_gap.json similarity index 100% rename from tests/baseline/empty_gap.json rename to tests/baselines/empty_gap.json diff --git a/tests/baseline/empty_marker.json b/tests/baselines/empty_marker.json similarity index 100% rename from tests/baseline/empty_marker.json rename to tests/baselines/empty_marker.json diff --git a/tests/baseline/empty_missingreference.json b/tests/baselines/empty_missingreference.json similarity index 100% rename from tests/baseline/empty_missingreference.json rename to tests/baselines/empty_missingreference.json diff --git a/tests/baseline/empty_rationaltime.json b/tests/baselines/empty_rationaltime.json similarity index 100% rename from tests/baseline/empty_rationaltime.json rename to tests/baselines/empty_rationaltime.json diff --git a/tests/baseline/empty_sequence.json b/tests/baselines/empty_sequence.json similarity index 100% rename from tests/baseline/empty_sequence.json rename to tests/baselines/empty_sequence.json diff --git a/tests/baseline/empty_serializeable_collection.json b/tests/baselines/empty_serializeable_collection.json similarity index 100% rename from tests/baseline/empty_serializeable_collection.json rename to tests/baselines/empty_serializeable_collection.json diff --git a/tests/baseline/empty_stack.json b/tests/baselines/empty_stack.json similarity index 100% rename from tests/baseline/empty_stack.json rename to tests/baselines/empty_stack.json diff --git a/tests/baseline/empty_timeline.json b/tests/baselines/empty_timeline.json similarity index 100% rename from tests/baseline/empty_timeline.json rename to tests/baselines/empty_timeline.json diff --git a/tests/baseline/empty_timerange.json b/tests/baselines/empty_timerange.json similarity index 100% rename from tests/baseline/empty_timerange.json rename to tests/baselines/empty_timerange.json diff --git a/tests/baseline/empty_timetransform.json b/tests/baselines/empty_timetransform.json similarity index 100% rename from tests/baseline/empty_timetransform.json rename to tests/baselines/empty_timetransform.json diff --git a/tests/baseline/empty_transition.json b/tests/baselines/empty_transition.json similarity index 100% rename from tests/baseline/empty_transition.json rename to tests/baselines/empty_transition.json diff --git a/tests/baselines/example.py b/tests/baselines/example.py new file mode 100644 index 000000000..7d8d4fe4c --- /dev/null +++ b/tests/baselines/example.py @@ -0,0 +1,52 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""This file is here to support the test_adapter_plugin unittest. +If you want to learn how to write your own adapter plugin, please read: +https://github.com/PixarAnimationStudios/OpenTimelineIO/wiki/How-to-Write-an-OpenTimelineIO-Adapter +""" + +import opentimelineio as otio + + +def read_from_file(filepath): + fake_tl = otio.schema.Timeline(name=filepath) + fake_tl.tracks.append(otio.schema.Sequence()) + fake_tl.tracks[0].append(otio.schema.Clip(name=filepath + "_clip")) + return fake_tl + + +def read_from_string(input_str): + return read_from_file(input_str) + + +# in practice, these will be in separate plugins, but for simplicity in the +# unit tests, we put this in the same file as the example adapter. +def link_media_reference(in_clip, media_linker_argument_map): + d = {'from_test_linker': True} + d.update(media_linker_argument_map) + return otio.media_reference.MissingReference( + name=in_clip.name + "_tweaked", + metadata=d + ) diff --git a/tests/baseline/media_linker_example.json b/tests/baselines/media_linker_example.json similarity index 100% rename from tests/baseline/media_linker_example.json rename to tests/baselines/media_linker_example.json diff --git a/tests/test_adapter_plugin.py b/tests/test_adapter_plugin.py index 7f56e8367..f2e1e98bb 100755 --- a/tests/test_adapter_plugin.py +++ b/tests/test_adapter_plugin.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest import os @@ -7,7 +29,7 @@ import opentimelineio as otio -__doc__ = """ Unit tests for the adapter plugin system. """ +"""Unit tests for the adapter plugin system.""" MANIFEST_PATH = "adapter_plugin_manifest.plugin_manifest" @@ -29,7 +51,7 @@ def setUp(self): self.adp = otio.adapters.otio_json.read_from_string(self.jsn) self.adp._json_path = os.path.join( baseline_reader.MODPATH, - "baseline", + "baselines", ADAPTER_PATH ) @@ -73,7 +95,7 @@ def test_plugin_adapter(self): def test_load_adapter_module(self): target = os.path.join( baseline_reader.MODPATH, - "baseline", + "baselines", "example.py" ) @@ -86,6 +108,11 @@ def test_load_adapter_module(self): # call through the convienence wrapper self.assertEqual(self.adp.read_from_file("foo").name, "foo") + def test_has_feature(self): + self.assertTrue(self.adp.has_feature("read")) + self.assertTrue(self.adp.has_feature("read_from_file")) + self.assertFalse(self.adp.has_feature("write")) + def test_run_media_linker_during_adapter(self): mfest = otio.plugins.ActiveManifest() @@ -148,9 +175,12 @@ def test_find_adapter_by_suffix(self): man.from_filepath("BLARG") adp = man.from_filepath("example") self.assertEqual(adp.module().read_from_file("path").name, "path") - self.assertEqual(man.adapter_module_from_suffix( - "example" - ).read_from_file("path").name, "path") + self.assertEqual( + man.adapter_module_from_suffix( + "example" + ).read_from_file("path").name, + "path" + ) def test_find_adapter_by_name(self): man = test_manifest() @@ -159,9 +189,12 @@ def test_find_adapter_by_name(self): man.from_name("BLARG") adp = man.from_name("example") self.assertEqual(adp.module().read_from_file("path").name, "path") - self.assertEqual(man.adapter_module_from_name( - "example" - ).read_from_file("path").name, "path") + self.assertEqual( + man.adapter_module_from_name("example").read_from_file( + "path" + ).name, + "path" + ) if __name__ == '__main__': diff --git a/tests/test_builtin_adapters.py b/tests/test_builtin_adapters.py index 3d322f0fe..f3e26a6e4 100755 --- a/tests/test_builtin_adapters.py +++ b/tests/test_builtin_adapters.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# """Test builtin adapters.""" diff --git a/tests/test_clip.py b/tests/test_clip.py index 3a16bc9c2..49caadf83 100755 --- a/tests/test_clip.py +++ b/tests/test_clip.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest diff --git a/tests/test_cmx_3600_adapter.py b/tests/test_cmx_3600_adapter.py index c1c1f9820..73d1b48c1 100755 --- a/tests/test_cmx_3600_adapter.py +++ b/tests/test_cmx_3600_adapter.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# """Test the CMX 3600 EDL adapter.""" @@ -62,8 +84,10 @@ def test_edl_read(self): marker = timeline.tracks[0][3].markers[0] self.assertEqual(marker.name, "ANIM FIX NEEDED") self.assertEqual(marker.metadata.get("cmx_3600").get("color"), "RED") - self.assertEqual(marker.marked_range.start_time, - otio.opentime.from_timecode("01:00:01:14")) + self.assertEqual( + marker.marked_range.start_time, + otio.opentime.from_timecode("01:00:01:14") + ) self.assertEqual(marker.color, otio.schema.MarkerColor.RED) self.assertEqual( diff --git a/tests/test_composable.py b/tests/test_composable.py index 0128b9f68..03a8794ae 100644 --- a/tests/test_composable.py +++ b/tests/test_composable.py @@ -1,4 +1,28 @@ -""" Test harness for Composable. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Test harness for Composable.""" import unittest diff --git a/tests/test_composition.py b/tests/test_composition.py index c135a7145..6a992887e 100755 --- a/tests/test_composition.py +++ b/tests/test_composition.py @@ -1,4 +1,27 @@ -#!/usr/bin/env python + +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest import os diff --git a/tests/test_effect.py b/tests/test_effect.py index 3d0785767..d2b26662c 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + import unittest import opentimelineio as otio diff --git a/tests/test_fcp7_xml_adapter.py b/tests/test_fcp7_xml_adapter.py index 3e19feebd..1be6a1f1e 100644 --- a/tests/test_fcp7_xml_adapter.py +++ b/tests/test_fcp7_xml_adapter.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python2.7 +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# """Test final cut pro xml.""" @@ -28,26 +50,44 @@ def test_read(self): self.assertTrue(timeline is not None) self.assertEqual(len(timeline.tracks), 8) - video_tracks = [t for t in timeline.tracks - if t.kind == otio.schema.SequenceKind.Video] - audio_tracks = [t for t in timeline.tracks - if t.kind == otio.schema.SequenceKind.Audio] + video_tracks = [ + t for t in timeline.tracks + if t.kind == otio.schema.SequenceKind.Video + ] + audio_tracks = [ + t for t in timeline.tracks + if t.kind == otio.schema.SequenceKind.Audio + ] self.assertEqual(len(video_tracks), 4) self.assertEqual(len(audio_tracks), 4) video_clip_names = ( (None, 'sc01_sh010_anim.mov'), - (None, 'sc01_sh010_anim.mov', None, 'sc01_sh020_anim.mov', - 'sc01_sh030_anim.mov', 'Cross Dissolve', None, 'sc01_sh010_anim'), + ( + None, + 'sc01_sh010_anim.mov', + None, + 'sc01_sh020_anim.mov', + 'sc01_sh030_anim.mov', + 'Cross Dissolve', + None, + 'sc01_sh010_anim' + ), (None, 'test_title'), - (None, 'sc01_master_layerA_sh030_temp.mov', 'Cross Dissolve', - 'sc01_sh010_anim.mov') + ( + None, + 'sc01_master_layerA_sh030_temp.mov', + 'Cross Dissolve', + 'sc01_sh010_anim.mov' + ) ) for n, track in enumerate(video_tracks): - self.assertTupleEqual(tuple(c.name for c in track), - video_clip_names[n]) + self.assertTupleEqual( + tuple(c.name for c in track), + video_clip_names[n] + ) audio_clip_names = ( (None, 'sc01_sh010_anim.mov', None, 'sc01_sh010_anim.mov'), @@ -57,13 +97,23 @@ def test_read(self): ) for n, track in enumerate(audio_tracks): - self.assertTupleEqual(tuple(c.name for c in track), - audio_clip_names[n]) + self.assertTupleEqual( + tuple(c.name for c in track), + audio_clip_names[n] + ) video_clip_durations = ( ((536, 30.0), (100, 30.0)), - ((13, 30.0), (100, 30.0), (52, 30.0), (157, 30.0), (235, 30.0), - ((19, 30.0), (0, 30.0)), (79, 30.0), (320, 30.0)), + ( + (13, 30.0), + (100, 30.0), + (52, 30.0), + (157, 30.0), + (235, 30.0), + ((19, 30.0), (0, 30.0)), + (79, 30.0), + (320, 30.0) + ), ((15, 30.0), (941, 30.0)), ((956, 30.0), (208, 30.0), ((12, 30.0), (13, 30.0)), (82, 30.0)) ) @@ -91,8 +141,14 @@ def test_read(self): audio_clip_durations = ( ((13, 30.0), (100, 30.0), (423, 30.0), (100, 30.0), (423, 30.0)), - ((335, 30.0), (170, 30.0), (131, 30.0), (294, 30.0), (34, 30.0), - (124, 30.0)), + ( + (335, 30.0), + (170, 30.0), + (131, 30.0), + (294, 30.0), + (34, 30.0), + (124, 30.0) + ), ((153, 30.0), (198, 30.0)), ((956, 30.0), (221, 30.0), (94, 30.0)) ) @@ -128,10 +184,13 @@ def test_read(self): clip_with_marker = video_tracks[1][4] clip_marker = clip_with_marker.markers[0] self.assertEqual(clip_marker.name, None) - self.assertEqual(clip_marker.marked_range.start_time, - otio.opentime.RationalTime(73, 30.0)) self.assertEqual( - clip_marker.metadata.get('fcp_xml', {}).get('comment'), None + clip_marker.marked_range.start_time, + otio.opentime.RationalTime(73, 30.0) + ) + self.assertEqual( + clip_marker.metadata.get('fcp_xml', {}).get('comment'), + None ) def test_backreference_generator_read(self): @@ -199,29 +258,34 @@ def test_roundtrip_mem2disk2mem(self): timeline.tracks.append(a0) - v0.extend([ - otio.schema.Clip( - name='test_clip1', - media_reference=video_reference, - source_range=otio.opentime.TimeRange( - otio.opentime.RationalTime(value=112, rate=24.0), - otio.opentime.RationalTime(value=40, rate=24.0) - ) - ), - otio.schema.Gap( - source_range=otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(value=60, rate=24.0) - ) - ), - otio.schema.Clip( - name='test_clip2', - media_reference=video_reference, - source_range=otio.opentime.TimeRange( - otio.opentime.RationalTime(value=123, rate=24.0), - otio.opentime.RationalTime(value=260, rate=24.0) + v0.extend( + [ + otio.schema.Clip( + name='test_clip1', + media_reference=video_reference, + source_range=otio.opentime.TimeRange( + otio.opentime.RationalTime(value=112, rate=24.0), + otio.opentime.RationalTime(value=40, rate=24.0) + ) + ), + otio.schema.Gap( + source_range=otio.opentime.TimeRange( + duration=otio.opentime.RationalTime( + value=60, + rate=24.0 + ) + ) + ), + otio.schema.Clip( + name='test_clip2', + media_reference=video_reference, + source_range=otio.opentime.TimeRange( + otio.opentime.RationalTime(value=123, rate=24.0), + otio.opentime.RationalTime(value=260, rate=24.0) + ) ) - ) - ]) + ] + ) v1.extend([ otio.schema.Gap( @@ -256,32 +320,42 @@ def test_roundtrip_mem2disk2mem(self): ]) timeline.tracks.markers.append( - otio.schema.Marker(name='test_timeline_marker', - marked_range=otio.opentime.TimeRange( - otio.opentime.RationalTime(123, 24.0) - ), - metadata={'fcp_xml': {'comment': 'my_comment'}}) + otio.schema.Marker( + name='test_timeline_marker', + marked_range=otio.opentime.TimeRange( + otio.opentime.RationalTime(123, 24.0) + ), + metadata={'fcp_xml': {'comment': 'my_comment'}} + ) ) v1[1].markers.append( - otio.schema.Marker(name='test_clip_marker', - marked_range=otio.opentime.TimeRange( - otio.opentime.RationalTime(125, 24.0) - ), - metadata={'fcp_xml': {'comment': 'my_comment'}}) + otio.schema.Marker( + name='test_clip_marker', + marked_range=otio.opentime.TimeRange( + otio.opentime.RationalTime(125, 24.0) + ), + metadata={'fcp_xml': {'comment': 'my_comment'}} + ) ) - result = otio.adapters.write_to_string(timeline, - adapter_name='fcp_xml') - new_timeline = otio.adapters.read_from_string(result, - adapter_name='fcp_xml') + result = otio.adapters.write_to_string( + timeline, + adapter_name='fcp_xml' + ) + new_timeline = otio.adapters.read_from_string( + result, + adapter_name='fcp_xml' + ) self.assertMultiLineEqual( otio.adapters.write_to_string( - new_timeline, adapter_name="otio_json" + new_timeline, + adapter_name="otio_json" ), otio.adapters.write_to_string( - timeline, adapter_name="otio_json" + timeline, + adapter_name="otio_json" ) ) diff --git a/tests/test_item.py b/tests/test_item.py index 5d1b20faa..fec892401 100755 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -1,6 +1,28 @@ -#!/usr/bin/env python - -""" Test harness for Item. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Test harness for Item.""" import unittest @@ -183,7 +205,8 @@ def test_metadata(self): def test_add_effect(self): tr = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(10, 1)) + duration=otio.opentime.RationalTime(10, 1) + ) it = otio.core.Item(source_range=tr) it.effects.append( otio.schema.Effect( @@ -200,7 +223,8 @@ def test_add_effect(self): def test_add_marker(self): tr = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(10, 1)) + duration=otio.opentime.RationalTime(10, 1) + ) it = otio.core.Item(source_range=tr) it.markers.append( otio.schema.Marker( @@ -218,7 +242,8 @@ def test_add_marker(self): def test_copy(self): tr = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(10, 1)) + duration=otio.opentime.RationalTime(10, 1) + ) it = otio.core.Item(source_range=tr, metadata={"foo": "bar"}) it.markers.append( otio.schema.Marker( @@ -255,7 +280,8 @@ def test_copy(self): def test_copy_library(self): tr = otio.opentime.TimeRange( - duration=otio.opentime.RationalTime(10, 1)) + duration=otio.opentime.RationalTime(10, 1) + ) it = otio.core.Item(source_range=tr, metadata={"foo": "bar"}) it.markers.append( otio.schema.Marker( diff --git a/tests/test_json_backend.py b/tests/test_json_backend.py index 2daa3c2a0..6f76cd68e 100755 --- a/tests/test_json_backend.py +++ b/tests/test_json_backend.py @@ -1,6 +1,28 @@ -#!/usr/bin/env python - -""" Unit tests for the JSON format OTIO Serializes to. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Unit tests for the JSON format OTIO Serializes to.""" import unittest import json diff --git a/tests/test_marker.py b/tests/test_marker.py index 811c98e23..d88740f24 100755 --- a/tests/test_marker.py +++ b/tests/test_marker.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest diff --git a/tests/test_media_linker.py b/tests/test_media_linker.py index 7749557e6..d422408f8 100644 --- a/tests/test_media_linker.py +++ b/tests/test_media_linker.py @@ -1,3 +1,27 @@ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + import unittest import os @@ -27,7 +51,7 @@ def setUp(self): self.mln = otio.adapters.otio_json.read_from_string(self.jsn) self.mln._json_path = os.path.join( baseline_reader.MODPATH, - "baseline", + "baselines", LINKER_PATH ) @@ -40,7 +64,7 @@ def test_load_adapter_module(self): target = os.path.join( baseline_reader.MODPATH, - "baseline", + "baselines", "example.py" ) diff --git a/tests/test_media_reference.py b/tests/test_media_reference.py index 0b9feda72..c4c5beb11 100755 --- a/tests/test_media_reference.py +++ b/tests/test_media_reference.py @@ -1,8 +1,28 @@ -#!/usr/bin/env python - -""" -Test harness for Media References. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Test harness for Media References.""" import opentimelineio as otio @@ -64,7 +84,8 @@ def test_filepath(self): def test_equality(self): filepath = otio.media_reference.External(target_url="/var/tmp/foo.mov") filepath2 = otio.media_reference.External( - target_url="/var/tmp/foo.mov") + target_url="/var/tmp/foo.mov" + ) self.assertEqual(filepath, filepath2) bl = otio.media_reference.MissingReference() diff --git a/tests/test_opentime.py b/tests/test_opentime.py index fcf958031..1076ff56d 100755 --- a/tests/test_opentime.py +++ b/tests/test_opentime.py @@ -1,8 +1,28 @@ -#!/usr/bin/env python - -""" -Test Harness for the otio.opentime library. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Test Harness for the otio.opentime library.""" import opentimelineio as otio @@ -627,7 +647,8 @@ def test_range_from_start_end_time(self): self.assertEqual( tr, otio.opentime.range_from_start_end_time( - tr.start_time, tr.end_time_exclusive()) + tr.start_time, tr.end_time_exclusive() + ) ) diff --git a/tests/test_sequence_algo.py b/tests/test_sequence_algo.py index 35f26df44..6c874823c 100644 --- a/tests/test_sequence_algo.py +++ b/tests/test_sequence_algo.py @@ -1,8 +1,28 @@ -#!/usr/bin/env python - -""" -Test file for the sequence algorithms library. -""" +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Test file for the sequence algorithms library.""" import unittest import copy diff --git a/tests/test_serializeable_collection.py b/tests/test_serializeable_collection.py index 78c0366f8..221ad52b6 100644 --- a/tests/test_serializeable_collection.py +++ b/tests/test_serializeable_collection.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest diff --git a/tests/test_serializeable_object.py b/tests/test_serializeable_object.py index 82d1dc867..d32e828ae 100755 --- a/tests/test_serializeable_object.py +++ b/tests/test_serializeable_object.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import opentimelineio as otio diff --git a/tests/test_timeline.py b/tests/test_timeline.py index 2a7e9bba5..d7f850ead 100755 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -1,4 +1,26 @@ -#!/usr/bin/env python +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# import unittest diff --git a/tests/test_transition.py b/tests/test_transition.py index 995e668e3..aadff209c 100644 --- a/tests/test_transition.py +++ b/tests/test_transition.py @@ -1,4 +1,28 @@ -""" Transition class test harness. """ +# +# Copyright 2017 Pixar Animation Studios +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +"""Transition class test harness.""" import unittest