diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 29296de..be41e6e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,23 @@ Changes ======= +1.3.0 +===== + +* **Fix:** Fixed a huge bug in 29.97 NDF and 59.97 NDF calculations introduced + in v1.2.3. + +* **Fix:** Fixed ``Timecode.framerate`` when it is given as ``23.98``. The + ``framerate`` attribute will not be forced to ``24`` and it will stay + ``23.98``. + +* **Update:** ``Timecode.tc_to_frames()`` method now accepts Timecode instances + with possibly different frame rates then the instance itself. + +* **Fix:** Fixed ``Timecode.div_frames()`` method. + +* **Update:** Test coverage has been increased to 100% (yay!) + 1.2.5 ===== diff --git a/tests/test_timecode.py b/tests/test_timecode.py index 3ae151e..aec405c 100644 --- a/tests/test_timecode.py +++ b/tests/test_timecode.py @@ -2,7 +2,7 @@ import unittest -from timecode import Timecode +from timecode import Timecode, TimecodeError class TimecodeTester(unittest.TestCase): @@ -21,198 +21,427 @@ def tearDownClass(cls): """ pass - def test_instance_creation(self): + def test_instance_creation_1(self): """test instance creation, none of these should raise any error """ - Timecode('23.976', '00:00:00:00') - Timecode('23.98', '00:00:00:00') - Timecode('24', '00:00:00:00') - Timecode('25', '00:00:00:00') - Timecode('29.97', '00:00:00;00') - Timecode('30', '00:00:00:00') - Timecode('50', '00:00:00:00') - Timecode('59.94', '00:00:00;00') - Timecode('60', '00:00:00:00') - Timecode('ms', '03:36:09.230') - Timecode('24', start_timecode=None, frames=12000) - - Timecode('23.976') - Timecode('23.98') - Timecode('24') - Timecode('25') - Timecode('29.97') - Timecode('30') - Timecode('50') - Timecode('59.94') - Timecode('60') - Timecode('ms') - - Timecode('23.976', 421729315) - Timecode('23.98', 421729315) - Timecode('24', 421729315) - Timecode('25', 421729315) - Timecode('29.97', 421729315) - Timecode('30', 421729315) - Timecode('50', 421729315) - Timecode('59.94', 421729315) - Timecode('60', 421729315) - Timecode('ms', 421729315) - - Timecode('24000/1000', '00:00:00:00') - Timecode('24000/1001', '00:00:00;00') - Timecode('30000/1000', '00:00:00:00') - Timecode('30000/1001', '00:00:00;00') - Timecode('60000/1000', '00:00:00:00') - Timecode('60000/1001', '00:00:00;00') - - Timecode((24000, 1000), '00:00:00:00') - Timecode((24000, 1001), '00:00:00;00') - Timecode((30000, 1000), '00:00:00:00') - Timecode((30000, 1001), '00:00:00;00') - Timecode((60000, 1000), '00:00:00:00') - Timecode((60000, 1001), '00:00:00;00') - - Timecode(24, frames=12000) - Timecode(23.976, '00:00:00:00') - Timecode(23.98, '00:00:00:00') - Timecode(24, '00:00:00:00') - Timecode(25, '00:00:00:00') - Timecode(29.97, '00:00:00;00') - Timecode(30, '00:00:00:00') - Timecode(50, '00:00:00:00') - Timecode(59.94, '00:00:00;00') - Timecode(60, '00:00:00:00') - Timecode(1000, '03:36:09.230') - Timecode(24, start_timecode=None, frames=12000) - - Timecode(23.976) - Timecode(23.98) - Timecode(24) - Timecode(25) - Timecode(29.97) - Timecode(30) - Timecode(50) - Timecode(59.94) - Timecode(60) - Timecode(1000) - Timecode(24, frames=12000) + tc = Timecode('23.976', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_2(self): + tc = Timecode('23.98', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_3(self): + tc = Timecode('24', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_4(self): + tc = Timecode('25', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_5(self): + tc = Timecode('29.97', '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_6(self): + tc = Timecode('30', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_7(self): + tc = Timecode('50', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_8(self): + tc = Timecode('59.94', '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_9(self): + tc = Timecode('60', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_10(self): + tc = Timecode('ms', '03:36:09.230') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_11(self): + tc = Timecode('24', start_timecode=None, frames=12000) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_12(self): + tc = Timecode('23.976') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_12(self): + tc = Timecode('23.98') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_13(self): + tc = Timecode('24') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_14(self): + tc = Timecode('25') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_15(self): + tc = Timecode('29.97') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_16(self): + tc = Timecode('30') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_17(self): + tc = Timecode('50') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_18(self): + tc = Timecode('59.94') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_19(self): + tc = Timecode('60') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_20(self): + tc = Timecode('ms') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_21(self): + tc = Timecode('23.976', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_22(self): + tc = Timecode('23.98', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_23(self): + tc = Timecode('24', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_24(self): + tc = Timecode('25', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_25(self): + tc = Timecode('29.97', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_26(self): + tc = Timecode('30', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_27(self): + tc = Timecode('50', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_28(self): + tc = Timecode('59.94', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_29(self): + tc = Timecode('60', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_30(self): + tc = Timecode('ms', 421729315) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_31(self): + tc = Timecode('24000/1000', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_31(self): + tc = Timecode('24000/1001', '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_32(self): + tc = Timecode('30000/1000', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_33(self): + tc = Timecode('30000/1001', '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_34(self): + tc = Timecode('60000/1000', '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_35(self): + tc = Timecode('60000/1001', '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_36(self): + tc = Timecode((24000, 1000), '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_37(self): + tc = Timecode((24000, 1001), '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_38(self): + tc = Timecode((30000, 1000), '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_39(self): + tc = Timecode((30000, 1001), '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_40(self): + tc = Timecode((60000, 1000), '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_41(self): + tc = Timecode((60000, 1001), '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_42(self): + tc = Timecode(24, frames=12000) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_43(self): + tc = Timecode(23.976, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_44(self): + tc = Timecode(23.98, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_45(self): + tc = Timecode(24, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_46(self): + tc = Timecode(25, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_47(self): + tc = Timecode(29.97, '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_48(self): + tc = Timecode(30, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_49(self): + tc = Timecode(50, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_50(self): + tc = Timecode(59.94, '00:00:00;00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_51(self): + tc = Timecode(60, '00:00:00:00') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_52(self): + tc = Timecode(1000, '03:36:09.230') + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_53(self): + tc = Timecode(24, start_timecode=None, frames=12000) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_54(self): + tc = Timecode(23.976) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_55(self): + tc = Timecode(23.98) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_56(self): + tc = Timecode(24) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_57(self): + tc = Timecode(25) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_58(self): + tc = Timecode(29.97) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_59(self): + tc = Timecode(30) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_60(self): + tc = Timecode(50) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_61(self): + tc = Timecode(59.94) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_62(self): + tc = Timecode(60) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_63(self): + tc = Timecode(1000) + self.assertIsInstance(tc, Timecode) + + def test_instance_creation_64(self): + tc = Timecode(24, frames=12000) + self.assertIsInstance(tc, Timecode) def test_2398_vs_23976(self): - timeobj1 = Timecode('23.98', '04:01:45:23') - timeobj2 = Timecode('23.976', '04:01:45:23') - self.assertEqual(timeobj1._frames, timeobj2._frames) - self.assertEqual(repr(timeobj1), repr(timeobj2)) + tc1 = Timecode('23.98', '04:01:45:23') + tc2 = Timecode('23.976', '04:01:45:23') + self.assertEqual(tc1._frames, tc2._frames) + self.assertEqual(repr(tc1), repr(tc2)) + + def test_repr_overload_1(self): + tc = Timecode('24', '01:00:00:00') + self.assertEqual('01:00:00:00', tc.__repr__()) + + def test_repr_overload_2(self): + tc = Timecode('23.98', '20:00:00:00') + self.assertEqual('20:00:00:00', tc.__repr__()) - def test_repr_overload(self): - timeobj = Timecode('24', '01:00:00:00') - self.assertEqual('01:00:00:00', timeobj.__repr__()) + def test_repr_overload_3(self): + tc = Timecode('29.97', '00:09:00;00') + self.assertEqual('00:08:59;28', tc.__repr__()) - timeobj = Timecode('23.98', '20:00:00:00') - self.assertEqual('20:00:00:00', timeobj.__repr__()) + def test_repr_overload_4(self): + tc = Timecode('29.97', '00:09:00:00', force_non_drop_frame=True) + self.assertEqual('00:09:00:00', tc.__repr__()) - timeobj = Timecode('29.97', '00:09:00;00') - self.assertEqual('00:08:59;28', timeobj.__repr__()) + def test_repr_overload_5(self): + tc = Timecode('30', '00:10:00:00') + self.assertEqual('00:10:00:00', tc.__repr__()) - timeobj = Timecode('29.97', '00:09:00:00', force_non_drop_frame=True) - self.assertEqual('00:08:59:14', timeobj.__repr__()) + def test_repr_overload_6(self): + tc = Timecode('60', '00:00:09:00') + self.assertEqual('00:00:09:00', tc.__repr__()) - timeobj = Timecode('30', '00:10:00:00') - self.assertEqual('00:10:00:00', timeobj.__repr__()) + def test_repr_overload_7(self): + tc = Timecode('59.94', '00:00:20;00') + self.assertEqual('00:00:20;00', tc.__repr__()) - timeobj = Timecode('60', '00:00:09:00') - self.assertEqual('00:00:09:00', timeobj.__repr__()) + def test_repr_overload_8(self): + tc = Timecode('59.94', '00:00:20;00') + self.assertNotEqual('00:00:20:00', tc.__repr__()) - timeobj = Timecode('59.94', '00:00:20;00') - self.assertEqual('00:00:20;00', timeobj.__repr__()) + def test_repr_overload_9(self): + tc = Timecode('ms', '00:00:00.900') + self.assertEqual('00:00:00.900', tc.__repr__()) - timeobj = Timecode('59.94', '00:00:20;00') - self.assertNotEqual('00:00:20:00', timeobj.__repr__()) + def test_repr_overload_10(self): + tc = Timecode('ms', '00:00:00.900') + self.assertNotEqual('00:00:00:900', tc.__repr__()) - timeobj = Timecode('ms', '00:00:00.900') - self.assertEqual('00:00:00.900', timeobj.__repr__()) + def test_repr_overload_11(self): + tc = Timecode('24', frames=49) + self.assertEqual('00:00:02:00', tc.__repr__()) - timeobj = Timecode('ms', '00:00:00.900') - self.assertNotEqual('00:00:00:900', timeobj.__repr__()) + def test_repr_overload_12(self): + tc = Timecode('59.94', '00:09:00:00', force_non_drop_frame=True) + self.assertEqual('00:09:00:00', tc.__repr__()) - timeobj = Timecode('24', frames=49) - self.assertEqual('00:00:02:00', timeobj.__repr__()) + def test_repr_overload_13(self): + tc1 = Timecode('59.94', frames=32401, force_non_drop_frame=True) + tc2 = Timecode('59.94', '00:09:00:00', force_non_drop_frame=True) + self.assertEqual(tc1, tc2) - def test_timecode_init(self): + def test_timecode_init_1(self): """testing several timecode initialization """ tc = Timecode('29.97') self.assertEqual('00:00:00;00', tc.__str__()) self.assertEqual(1, tc._frames) + def test_timecode_init_2(self): tc = Timecode('29.97', force_non_drop_frame=True) self.assertEqual('00:00:00:00', tc.__str__()) self.assertEqual(1, tc._frames) - tc = Timecode('29.97', force_non_drop_frame=True) - + def test_timecode_init_3(self): tc = Timecode('29.97', '00:00:00;01') self.assertEqual(2, tc._frames) + def test_timecode_init_4(self): tc = Timecode('29.97', '00:00:00:01', force_non_drop_frame=True) self.assertEqual(2, tc._frames) + def test_timecode_init_5(self): tc = Timecode('29.97', '03:36:09;23') self.assertEqual(388704, tc._frames) + def test_timecode_init_6(self): tc = Timecode('29.97', '03:36:09:23', force_non_drop_frame=True) - self.assertEqual(388705, tc._frames) + self.assertEqual(389094, tc._frames) + def test_timecode_init_7(self): tc = Timecode('29.97', '03:36:09;23') self.assertEqual(388704, tc._frames) + def test_timecode_init_8(self): tc = Timecode('30', '03:36:09:23') self.assertEqual(389094, tc._frames) + def test_timecode_init_9(self): tc = Timecode('25', '03:36:09:23') self.assertEqual(324249, tc._frames) + def test_timecode_init_10(self): tc = Timecode('59.94', '03:36:09;23') self.assertEqual(777384, tc._frames) + def test_timecode_init_11(self): tc = Timecode('60', '03:36:09:23') self.assertEqual(778164, tc._frames) + def test_timecode_init_12(self): tc = Timecode('59.94', '03:36:09;23') self.assertEqual(777384, tc._frames) + def test_timecode_init_13(self): tc = Timecode('23.98', '03:36:09:23') self.assertEqual(311280, tc._frames) + def test_timecode_init_14(self): tc = Timecode('24', '03:36:09:23') self.assertEqual(311280, tc._frames) + def test_timecode_init_15(self): tc = Timecode('ms', '03:36:09.230') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(230, tc.frs) + def test_timecode_init_16(self): tc = Timecode('24', frames=12000) self.assertEqual('00:08:19:23', tc.__str__()) + def test_timecode_init_17(self): tc = Timecode('29.97', frames=2589408) self.assertEqual('23:59:59;29', tc.__str__()) + def test_timecode_init_18(self): tc = Timecode('29.97', frames=2589409) self.assertEqual('00:00:00;00', tc.__str__()) + def test_timecode_init_19(self): tc = Timecode('29.97', frames=2589409, force_non_drop_frame=True) self.assertEqual('00:00:00:00', tc.__str__()) + def test_timecode_init_20(self): tc = Timecode('59.94', frames=5178816) self.assertEqual('23:59:59;59', tc.__str__()) + def test_timecode_init_21(self): tc = Timecode('59.94', frames=5178817) self.assertEqual('00:00:00;00', tc.__str__()) + def test_timecode_init_22(self): tc = Timecode('25', 421729315) self.assertEqual('19:23:14:23', tc.__str__()) + def test_timecode_init_23(self): tc = Timecode('29.97', 421729315) self.assertEqual('19:23:14;23', tc.__str__()) self.assertTrue(tc.drop_frame) @@ -229,7 +458,7 @@ def test_start_seconds_argument_is_zero(self): '``start_seconds`` argument can not be 0' ) - def test_frame_to_tc(self): + def test_frame_to_tc_1(self): tc = Timecode('29.97', '00:00:00;01') self.assertEqual(0, tc.hrs) self.assertEqual(0, tc.mins) @@ -237,66 +466,77 @@ def test_frame_to_tc(self): self.assertEqual(1, tc.frs) self.assertEqual('00:00:00;01', tc.__str__()) + def test_frame_to_tc_2(self): tc = Timecode('29.97', '03:36:09;23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_3(self): tc = Timecode('29.97', '03:36:09;23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_4(self): tc = Timecode('30', '03:36:09:23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_5(self): tc = Timecode('25', '03:36:09:23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_6(self): tc = Timecode('59.94', '03:36:09;23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_7(self): tc = Timecode('60', '03:36:09:23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_8(self): tc = Timecode('59.94', '03:36:09;23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_9(self): tc = Timecode('23.98', '03:36:09:23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_10(self): tc = Timecode('24', '03:36:09:23') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(23, tc.frs) + def test_frame_to_tc_11(self): tc = Timecode('ms', '03:36:09.230') self.assertEqual(3, tc.hrs) self.assertEqual(36, tc.mins) self.assertEqual(9, tc.secs) self.assertEqual(230, tc.frs) + def test_frame_to_tc_12(self): tc = Timecode('24', frames=12000) self.assertEqual('00:08:19:23', tc.__str__()) self.assertEqual(0, tc.hrs) @@ -304,55 +544,71 @@ def test_frame_to_tc(self): self.assertEqual(19, tc.secs) self.assertEqual(23, tc.frs) - def test_tc_to_frame_test_in_2997(self): + def test_tc_to_frame_test_in_2997_1(self): """testing if timecode to frame conversion is ok in 2997 """ tc = Timecode('29.97', '00:00:00;00') self.assertEqual(tc._frames, 1) + def test_tc_to_frame_test_in_2997_2(self): tc = Timecode('29.97', '00:00:00;21') self.assertEqual(tc._frames, 22) + def test_tc_to_frame_test_in_2997_3(self): tc = Timecode('29.97', '00:00:00;29') self.assertEqual(tc._frames, 30) + def test_tc_to_frame_test_in_2997_4(self): tc = Timecode('29.97', '00:00:00;60') self.assertEqual(tc._frames, 61) + def test_tc_to_frame_test_in_2997_5(self): tc = Timecode('29.97', '00:00:01;00') self.assertEqual(tc._frames, 31) + def test_tc_to_frame_test_in_2997_6(self): tc = Timecode('29.97', '00:00:10;00') self.assertEqual(tc._frames, 301) + def test_tc_to_frame_test_in_2997_7(self): # test with non existing timecodes tc = Timecode('29.97', '00:01:00;00') self.assertEqual(1799, tc._frames) self.assertEqual('00:00:59;28', tc.__str__()) + def test_tc_to_frame_test_in_2997_8(self): # test the limit tc = Timecode('29.97', '23:59:59;29') self.assertEqual(2589408, tc._frames) - def test_force_drop_frame_to_false(self): + def test_force_drop_frame_to_true_1(self): tc = Timecode('29.97', '01:00:00:00', force_non_drop_frame=True) - self.assertEqual('00:59:56:12', tc.__repr__()) + self.assertEqual('01:00:00:00', tc.__repr__()) - def test_drop_frame(self): + def test_force_drop_frame_to_true_2(self): + tc = Timecode('29.97', '01:00:00:00', force_non_drop_frame=True) + self.assertEqual('01:00:00:00', tc.__repr__()) + + def test_drop_frame_1(self): tc = Timecode('29.97', '13:36:59;29') timecode = tc.next() self.assertEqual("13:37:00;02", timecode.__str__()) + def test_drop_frame_2(self): tc = Timecode('59.94', '13:36:59;59') self.assertEqual("13:36:59;59", tc.__str__()) + def test_drop_frame_3(self): + tc = Timecode('59.94', '13:36:59;59') timecode = tc.next() self.assertEqual("13:37:00;04", timecode.__str__()) + def test_drop_frame_4(self): tc = Timecode('59.94', '13:39:59;59') timecode = tc.next() self.assertEqual("13:40:00;00", timecode.__str__()) + def test_drop_frame_5(self): tc = Timecode('29.97', '13:39:59;29') timecode = tc.next() self.assertEqual("13:40:00;00", timecode.__str__()) @@ -387,7 +643,15 @@ def test_setting_frame_rate_to_ms_or_1000_forces_drop_frame(self): tc = Timecode('1000') self.assertTrue(tc.ms_frame) - def test_iteration(self): + def test_framerate_argument_is_frames(self): + """testing if setting the framerate argument to 'frames' will set the + integer frame rate to 1 + """ + tc = Timecode('frames') + self.assertEqual(tc.framerate, 'frames') + self.assertEqual(tc._int_framerate, 1) + + def test_iteration_1(self): tc = Timecode('29.97', '03:36:09;23') assert tc == "03:36:09;23" @@ -408,6 +672,7 @@ def test_iteration(self): # assert t == '' # assert tc.frames == 388764 + def test_iteration_2(self): tc = Timecode('29.97', '03:36:09;23') for x in range(60): t = tc.next() @@ -415,6 +680,7 @@ def test_iteration(self): assert t == "03:36:11;23" self.assertEqual(388764, tc._frames) + def test_iteration_3(self): tc = Timecode('30', '03:36:09:23') for x in range(60): t = tc.next() @@ -422,6 +688,7 @@ def test_iteration(self): assert t == "03:36:11:23" self.assertEqual(389154, tc._frames) + def test_iteration_4(self): tc = Timecode('25', '03:36:09:23') for x in range(60): t = tc.next() @@ -429,6 +696,7 @@ def test_iteration(self): assert t == "03:36:12:08" self.assertEqual(324309, tc._frames) + def test_iteration_5(self): tc = Timecode('59.94', '03:36:09;23') for x in range(60): t = tc.next() @@ -436,6 +704,7 @@ def test_iteration(self): assert t == "03:36:10;23" self.assertEqual(777444, tc._frames) + def test_iteration_6(self): tc = Timecode('60', '03:36:09:23') for x in range(60): t = tc.next() @@ -443,6 +712,7 @@ def test_iteration(self): assert t == "03:36:10:23" self.assertEqual(778224, tc._frames) + def test_iteration_7(self): tc = Timecode('59.94', '03:36:09:23') for x in range(60): t = tc.next() @@ -450,6 +720,7 @@ def test_iteration(self): assert t == "03:36:10:23" self.assertEqual(777444, tc._frames) + def test_iteration_8(self): tc = Timecode('23.98', '03:36:09:23') for x in range(60): t = tc.next() @@ -457,6 +728,7 @@ def test_iteration(self): assert t == "03:36:12:11" self.assertEqual(311340, tc._frames) + def test_iteration_9(self): tc = Timecode('24', '03:36:09:23') for x in range(60): t = tc.next() @@ -464,6 +736,7 @@ def test_iteration(self): assert t == "03:36:12:11" self.assertEqual(311340, tc._frames) + def test_iteration_10(self): tc = Timecode('ms', '03:36:09.230') for x in range(60): t = tc.next() @@ -471,6 +744,7 @@ def test_iteration(self): assert t == '03:36:09.290' self.assertEqual(12969291, tc._frames) + def test_iteration_11(self): tc = Timecode('24', frames=12000) for x in range(60): t = tc.next() @@ -478,7 +752,7 @@ def test_iteration(self): assert t == "00:08:22:11" self.assertEqual(12060, tc._frames) - def test_op_overloads_add(self): + def test_op_overloads_add_1(self): tc = Timecode('29.97', '03:36:09;23') tc2 = Timecode('29.97', '00:00:29;23') d = tc + tc2 @@ -488,6 +762,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39;17", f.__str__()) self.assertEqual(389598, f._frames) + def test_op_overloads_add_2(self): tc = Timecode('29.97', '03:36:09;23') tc2 = Timecode('29.97', '00:00:29;23') d = tc + tc2 @@ -497,6 +772,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39;17", f.__str__()) self.assertEqual(389598, f._frames) + def test_op_overloads_add_3(self): tc = Timecode('30', '03:36:09:23') tc2 = Timecode('30', '00:00:29:23') d = tc + tc2 @@ -506,6 +782,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39:17", f.__str__()) self.assertEqual(389988, f._frames) + def test_op_overloads_add_4(self): tc = Timecode('25', '03:36:09:23') tc2 = Timecode('25', '00:00:29:23') self.assertEqual(749, tc2._frames) @@ -516,6 +793,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39:22", f.__str__()) self.assertEqual(324998, f._frames) + def test_op_overloads_add_5(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') self.assertEqual(1764, tc2._frames) @@ -526,6 +804,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:38;47", f.__str__()) self.assertEqual(779148, f._frames) + def test_op_overloads_add_6(self): tc = Timecode('60', '03:36:09:23') tc2 = Timecode('60', '00:00:29:23') self.assertEqual(1764, tc2._frames) @@ -536,6 +815,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:38:47", f.__str__()) self.assertEqual(779928, f._frames) + def test_op_overloads_add_7(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') self.assertEqual(1764, tc2._frames) @@ -546,6 +826,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:38;47", f.__str__()) self.assertEqual(779148, f._frames) + def test_op_overloads_add_8(self): tc = Timecode('23.98', '03:36:09:23') tc2 = Timecode('23.98', '00:00:29:23') self.assertEqual(720, tc2._frames) @@ -556,6 +837,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39:23", f.__str__()) self.assertEqual(312000, f._frames) + def test_op_overloads_add_9(self): tc = Timecode('23.98', '03:36:09:23') tc2 = Timecode('23.98', '00:00:29:23') self.assertEqual(720, tc2._frames) @@ -566,6 +848,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:39:23", f.__str__()) self.assertEqual(312000, f._frames) + def test_op_overloads_add_10(self): tc = Timecode('ms', '03:36:09.230') tc2 = Timecode('ms', '01:06:09.230') self.assertEqual(3969231, tc2._frames) @@ -576,6 +859,7 @@ def test_op_overloads_add(self): self.assertEqual("03:36:09.950", f.__str__()) self.assertEqual(12969951, f._frames) + def test_op_overloads_add_11(self): tc = Timecode('24', frames=12000) tc2 = Timecode('24', frames=485) self.assertEqual(485, tc2._frames) @@ -597,7 +881,86 @@ def test_add_with_two_different_frame_rates(self): self.assertEqual(12, tc3._frames) assert tc3 == '00:00:00;11' - def test_frame_number_attribute_value_is_correctly_calculated(self): + def test_add_with_non_suitable_class_instance(self): + """testing if a TimecodeError will be raised when the other class is + not suitable for __add__ operation + """ + tc1 = Timecode('24', '00:00:01:00') + with self.assertRaises(TimecodeError) as cm: + result = tc1 + 'not suitable' + + self.assertEqual( + 'Type str not supported for arithmetic.', + str(cm.exception) + ) + + def test_sub_with_non_suitable_class_instance(self): + """testing if a TimecodeError will be raised when the other class is + not suitable for __sub__ operation + """ + tc1 = Timecode('24', '00:00:01:00') + with self.assertRaises(TimecodeError) as cm: + result = tc1 - 'not suitable' + + self.assertEqual( + 'Type str not supported for arithmetic.', + str(cm.exception) + ) + + def test_mul_with_non_suitable_class_instance(self): + """testing if a TimecodeError will be raised when the other class is + not suitable for __mul__ operation + """ + tc1 = Timecode('24', '00:00:01:00') + with self.assertRaises(TimecodeError) as cm: + result = tc1 * 'not suitable' + + self.assertEqual( + 'Type str not supported for arithmetic.', + str(cm.exception) + ) + + def test_div_with_non_suitable_class_instance(self): + """testing if a TimecodeError will be raised when the other class is + not suitable for __div__ operation + """ + tc1 = Timecode('24', '00:00:01:00') + with self.assertRaises(TimecodeError) as cm: + result = tc1 / 'not suitable' + self.assertEqual( + 'Type str not supported for arithmetic.', + str(cm.exception) + ) + + def test_truediv_with_non_suitable_class_instance_2(self): + """testing if a TimecodeError will be raised when the other class is + not suitable for __div__ operation + """ + tc1 = Timecode('24', '00:00:01:00') + with self.assertRaises(TimecodeError) as cm: + result = tc1 / 32.4 + self.assertEqual( + 'Type float not supported for arithmetic.', + str(cm.exception) + ) + + def test_div_method_working_properly_1(self): + """testing if the __div__ method is working properly + """ + tc1 = Timecode('24', frames=100) + tc2 = Timecode('24', frames=10) + tc3 = tc1 / tc2 + self.assertEqual(tc3.frames, 10) + self.assertEqual(tc3, '00:00:00:09') + + def test_div_method_working_properly_2(self): + """testing if the __div__ method is working properly + """ + tc1 = Timecode('24', '00:00:10:00') + tc2 = tc1 / 10 + self.assertEqual(tc2, '00:00:00:23') + + def test_frame_number_attribute_value_is_correctly_calculated_1(self): """testing if the Timecode.frame_number attribute is correctly calculated """ @@ -605,31 +968,55 @@ def test_frame_number_attribute_value_is_correctly_calculated(self): self.assertEqual(1, tc1._frames) self.assertEqual(0, tc1.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_2(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc2 = Timecode('24', '00:00:01:00') self.assertEqual(25, tc2._frames) self.assertEqual(24, tc2.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_3(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc3 = Timecode('29.97', '00:01:00;00') self.assertEqual(1799, tc3._frames) self.assertEqual(1798, tc3.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_4(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc4 = Timecode('30', '00:01:00:00') self.assertEqual(1801, tc4._frames) self.assertEqual(1800, tc4.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_5(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc5 = Timecode('50', '00:01:00:00') self.assertEqual(3001, tc5._frames) self.assertEqual(3000, tc5.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_6(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc6 = Timecode('59.94', '00:01:00;00') self.assertEqual(3597, tc6._frames) self.assertEqual(3596, tc6.frame_number) + def test_frame_number_attribute_value_is_correctly_calculated_7(self): + """testing if the Timecode.frame_number attribute is correctly + calculated + """ tc7 = Timecode('60', '00:01:00:00') self.assertEqual(3601, tc7._frames) self.assertEqual(3600, tc7.frame_number) - def test_op_overloads_subtract(self): + def test_op_overloads_subtract_1(self): tc = Timecode('29.97', '03:36:09;23') tc2 = Timecode('29.97', '00:00:29;23') self.assertEqual(894, tc2._frames) @@ -640,6 +1027,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39;27", f.__str__()) self.assertEqual(387810, f._frames) + def test_op_overloads_subtract_2(self): tc = Timecode('29.97', '03:36:09;23') tc2 = Timecode('29.97', '00:00:29;23') d = tc - tc2 @@ -649,6 +1037,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39;27", f.__str__()) self.assertEqual(387810, f._frames) + def test_op_overloads_subtract_3(self): tc = Timecode('30', '03:36:09:23') tc2 = Timecode('30', '00:00:29:23') d = tc - tc2 @@ -658,6 +1047,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39:29", f.__str__()) self.assertEqual(388200, f._frames) + def test_op_overloads_subtract_4(self): tc = Timecode('25', '03:36:09:23') tc2 = Timecode('25', '00:00:29:23') self.assertEqual(749, tc2._frames) @@ -668,6 +1058,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39:24", f.__str__()) self.assertEqual(323500, f._frames) + def test_op_overloads_subtract_5(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') self.assertEqual(1764, tc2._frames) @@ -678,6 +1069,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39;55", f.__str__()) self.assertEqual(775620, f._frames) + def test_op_overloads_subtract_6(self): tc = Timecode('60', '03:36:09:23') tc2 = Timecode('60', '00:00:29:23') self.assertEqual(1764, tc2._frames) @@ -688,6 +1080,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39:59", f.__str__()) self.assertEqual(776400, f._frames) + def test_op_overloads_subtract_7(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') d = tc - tc2 @@ -697,6 +1090,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39;55", f.__str__()) self.assertEqual(775620, f._frames) + def test_op_overloads_subtract_8(self): tc = Timecode('23.98', '03:36:09:23') tc2 = Timecode('23.98', '00:00:29:23') self.assertEqual(720, tc2._frames) @@ -707,6 +1101,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39:23", f.__str__()) self.assertEqual(310560, f._frames) + def test_op_overloads_subtract_9(self): tc = Timecode('23.98', '03:36:09:23') tc2 = Timecode('23.98', '00:00:29:23') d = tc - tc2 @@ -716,6 +1111,7 @@ def test_op_overloads_subtract(self): self.assertEqual("03:35:39:23", f.__str__()) self.assertEqual(310560, f._frames) + def test_op_overloads_subtract_10(self): tc = Timecode('ms', '03:36:09.230') tc2 = Timecode('ms', '01:06:09.230') self.assertEqual(3969231, tc2._frames) @@ -726,6 +1122,7 @@ def test_op_overloads_subtract(self): self.assertEqual("02:29:59.999", f.__str__()) self.assertEqual(9000000, f._frames) + def test_op_overloads_subtract_11(self): tc = Timecode('24', frames=12000) tc2 = Timecode('24', frames=485) self.assertEqual(485, tc2._frames) @@ -736,7 +1133,7 @@ def test_op_overloads_subtract(self): self.assertEqual("00:07:59:18", f.__str__()) self.assertEqual(11515, f._frames) - def test_op_overloads_mult(self): + def test_op_overloads_mult_1(self): tc = Timecode('29.97', '00:00:09;23') tc2 = Timecode('29.97', '00:00:29;23') d = tc * tc2 @@ -746,6 +1143,7 @@ def test_op_overloads_mult(self): self.assertEqual("00:00:39;05", f.__str__()) self.assertEqual(1176, f._frames) + def test_op_overloads_mult_2(self): tc = Timecode('29.97', '00:00:09;23') tc2 = Timecode('29.97', '00:00:29;23') d = tc * tc2 @@ -755,6 +1153,7 @@ def test_op_overloads_mult(self): self.assertEqual("00:00:39;05", f.__str__()) self.assertEqual(1176, f._frames) + def test_op_overloads_mult_3(self): tc = Timecode('30', '03:36:09:23') tc2 = Timecode('30', '00:00:29:23') d = tc * tc2 @@ -764,6 +1163,7 @@ def test_op_overloads_mult(self): self.assertEqual("04:50:01:05", f.__str__()) self.assertEqual(347850036, f._frames) + def test_op_overloads_mult_4(self): tc = Timecode('25', '03:36:09:23') tc2 = Timecode('25', '00:00:29:23') self.assertEqual(749, tc2._frames) @@ -774,6 +1174,7 @@ def test_op_overloads_mult(self): self.assertEqual("10:28:20:00", f.__str__()) self.assertEqual(242862501, f._frames) + def test_op_overloads_mult_5(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') self.assertEqual(1764, tc2._frames) @@ -784,6 +1185,7 @@ def test_op_overloads_mult(self): self.assertEqual("18:59:27;35", f.__str__()) self.assertEqual(1371305376, f._frames) + def test_op_overloads_mult_6(self): tc = Timecode('60', '03:36:09:23') tc2 = Timecode('60', '00:00:29:23') self.assertEqual(1764, tc2._frames) @@ -794,6 +1196,7 @@ def test_op_overloads_mult(self): self.assertEqual("19:00:21:35", f.__str__()) self.assertEqual(1372681296, f._frames) + def test_op_overloads_mult_7(self): tc = Timecode('59.94', '03:36:09;23') tc2 = Timecode('59.94', '00:00:29;23') d = tc * tc2 @@ -803,17 +1206,19 @@ def test_op_overloads_mult(self): self.assertEqual("18:59:27;35", f.__str__()) self.assertEqual(1371305376, f._frames) + def test_op_overloads_mult_8(self): tc = Timecode('23.98', '03:36:09:23') tc2 = Timecode('23.98', '00:00:29:23') self.assertEqual(tc._frames, 311280) self.assertEqual(tc2._frames, 720) - d = tc * tc2 - f = tc * 720 - self.assertEqual(224121600, d._frames) - self.assertEqual("01:59:59:23", d.__str__()) - self.assertEqual(224121600, f._frames) - self.assertEqual("01:59:59:23", f.__str__()) - + tc3 = tc * tc2 + tc4 = tc * 720 + self.assertEqual(224121600, tc3._frames) + self.assertEqual("04:09:35:23", tc3.__str__()) + self.assertEqual(224121600, tc4._frames) + self.assertEqual("04:09:35:23", tc4.__str__()) + + def test_op_overloads_mult_9(self): tc = Timecode('ms', '03:36:09.230') tc2 = Timecode('ms', '01:06:09.230') self.assertEqual(3969231, tc2._frames) @@ -824,6 +1229,7 @@ def test_op_overloads_mult(self): self.assertEqual("17:22:11.360", f.__str__()) self.assertEqual(51477873731361, f._frames) + def test_op_overloads_mult_10(self): tc = Timecode('24', frames=12000) tc2 = Timecode('24', frames=485) self.assertEqual(485, tc2._frames) @@ -834,6 +1240,15 @@ def test_op_overloads_mult(self): self.assertEqual("19:21:39:23", f.__str__()) self.assertEqual(5820000, f._frames) + def test_op_overloads_mult_11(self): + """when two Timecode instances are multiplied the framerate of the + resultant timecode should be the same with the left side + """ + tc = Timecode('23.98', '03:36:09:23') + tc2 = Timecode('23.98', '00:00:29:23') + tc3 = tc * tc2 + self.assertEqual(tc3.framerate, '23.98') + def test_24_hour_limit_in_24fps(self): """testing if the timecode will loop back to 00:00:00:00 after 24 hours in 24 fps @@ -954,66 +1369,88 @@ def test_framerate_can_be_changed(self): self.assertEqual('00:00:08:03', tc1.__str__()) self.assertEqual(100, tc1._frames) - def test_rational_framerate_conversions(self): + def test_rational_framerate_conversions_1(self): tc = Timecode('24000/1000', '00:00:00:00') self.assertEqual(tc.framerate, '24') self.assertEqual(tc._int_framerate, 24) + def test_rational_framerate_conversions_2(self): + tc = Timecode('24000/1001', '00:00:00;00') + self.assertEqual(tc.framerate, '23.98') + + def test_rational_framerate_conversions_2a(self): tc = Timecode('24000/1001', '00:00:00;00') - self.assertEqual(tc.framerate, '24') self.assertEqual(tc._int_framerate, 24) + def test_rational_framerate_conversions_3(self): tc = Timecode('30000/1000', '00:00:00:00') self.assertEqual(tc.framerate, '30') self.assertEqual(tc._int_framerate, 30) + def test_rational_framerate_conversions_4(self): tc = Timecode('30000/1001', '00:00:00;00') self.assertEqual(tc.framerate, '29.97') self.assertEqual(tc._int_framerate, 30) + def test_rational_framerate_conversions_5(self): tc = Timecode('60000/1000', '00:00:00:00') self.assertEqual(tc.framerate, '60') self.assertEqual(tc._int_framerate, 60) + def test_rational_framerate_conversions_6(self): tc = Timecode('60000/1001', '00:00:00;00') self.assertEqual(tc.framerate, '59.94') self.assertEqual(tc._int_framerate, 60) + def test_rational_framerate_conversions_7(self): tc = Timecode((60000, 1001), '00:00:00;00') self.assertEqual(tc.framerate, '59.94') self.assertEqual(tc._int_framerate, 60) - def test_rational_frame_delimiter(self): + def test_rational_frame_delimiter_1(self): tc = Timecode('24000/1000', frames=1) self.assertFalse(';' in tc.__repr__()) + def test_rational_frame_delimiter_2(self): tc = Timecode('24000/1001', frames=1) self.assertFalse(';' in tc.__repr__()) + def test_rational_frame_delimiter_3(self): tc = Timecode('30000/1001', frames=1) self.assertTrue(';' in tc.__repr__()) - def test_ms_vs_fraction_frames(self): + def test_ms_vs_fraction_frames_1(self): tc1 = Timecode('ms', '00:00:00.040') self.assertTrue(tc1.ms_frame) self.assertFalse(tc1.fraction_frame) + def test_ms_vs_fraction_frames_2(self): tc2 = Timecode(24, '00:00:00.042') self.assertTrue(tc2.fraction_frame) self.assertFalse(tc2.ms_frame) + def test_ms_vs_fraction_frames_3(self): + tc1 = Timecode('ms', '00:00:00.040') + tc2 = Timecode(24, '00:00:00.042') self.assertNotEqual(tc1, tc2) + def test_ms_vs_fraction_frames_4(self): + tc1 = Timecode('ms', '00:00:00.040') + tc2 = Timecode(24, '00:00:00.042') self.assertEqual(tc1.frame_number, 40) self.assertEqual(tc2.frame_number, 1) - def test_toggle_fractional_frame(self): + def test_toggle_fractional_frame_1(self): tc = Timecode(24, 421729315) self.assertEqual(tc.__repr__(), '19:23:14:23') + def test_toggle_fractional_frame_2(self): + tc = Timecode(24, 421729315) tc.set_fractional(True) self.assertEqual(tc.__repr__(), '19:23:14.958') + def test_toggle_fractional_frame_3(self): + tc = Timecode(24, 421729315) tc.set_fractional(False) self.assertEqual(tc.__repr__(), '19:23:14:23') @@ -1157,3 +1594,134 @@ def test_bug_report_32(self): seconds = 500 tc1 = Timecode(framerate, start_seconds=seconds) self.assertEqual(seconds, tc1.float) + + def test_set_timecode_method(self): + """testing if the set_timecode method is working properly + """ + tc1 = Timecode('24') + self.assertEqual(tc1.frames, 1) + self.assertEqual(tc1, '00:00:00:00') + + tc2 = Timecode('29.97', frames=1000) + self.assertEqual(tc2.frames, 1000) + + tc1.set_timecode(tc2.__repr__()) # this is interpreted as 24 + self.assertEqual(tc1.frames, 802) + + tc1.set_timecode(tc2) # this should be interpreted as 29.97 and 1000 frames + self.assertEqual(tc1.frames, 1000) + + def test_iter_method(self): + """testing the __iter__ method + """ + tc = Timecode('24', '01:00:00:00') + for a in tc: + self.assertEqual(a, tc) + + def test_back_method_returns_a_timecode_instance(self): + """testing if the back method returns a Timecode instance + """ + tc = Timecode('24', '01:00:00:00') + self.assertIsInstance(tc.back(), Timecode) + + def test_back_method_returns_the_instance_itself(self): + """testing if the back method returns the Timecode instance itself + """ + tc = Timecode('24', '01:00:00:00') + self.assertIs(tc.back(), tc) + + def test_back_method_reduces_frames_by_one(self): + """testing if the back method reduces the Timecode.frames by one + """ + tc = Timecode('24', '01:00:00:00') + frames = tc.frames + self.assertEqual(tc.back().frames, frames - 1) + + def test_mult_frames_method_is_working_properly(self): + """testing if the mult_frames method is working properly + """ + tc = Timecode('24') + tc.mult_frames(10) + self.assertEqual(tc.frames, 10) + self.assertEqual(tc.__repr__(), '00:00:00:09') + + def test_div_frames_method_is_working_properly(self): + """testing if the div_frames method is working properly + """ + tc = Timecode('24', '00:00:00:09') + self.assertEqual(tc.frames, 10) + tc.div_frames(10) + self.assertEqual(tc.frames, 1) + self.assertEqual(tc.__repr__(), '00:00:00:00') + + def test_eq_method_with_integers(self): + """testing if comparing the Timecode with integers are working properly + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc == 241) + + def test_ge_method_with_strings(self): + """testing the __ge__ method with strings + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc >= '00:00:09:00') + self.assertTrue(tc >= '00:00:10:00') + + def test_ge_method_with_integers(self): + """testing the __ge__ method with integers + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc >= 230) + self.assertTrue(tc >= 241) + + def test_gt_method_with_strings(self): + """testing the __gt__ method with strings + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc > '00:00:09:00') + + def test_gt_method_with_integers(self): + """testing the __gt__ method with integers + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc > 230) + + def test_le_method_with_strings(self): + """testing the __le__ method with strings + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc <= '00:00:11:00') + self.assertTrue(tc <= '00:00:10:00') + + def test_le_method_with_integers(self): + """testing the __le__ method with integers + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc <= 250) + self.assertTrue(tc <= 241) + + def test_lt_method_with_strings(self): + """testing the __lt__ method with strings + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc < '00:00:11:00') + + def test_lt_method_with_integers(self): + """testing the __lt__ method with integers + """ + tc = Timecode('24', '00:00:10:00') + self.assertTrue(tc < 250) + + def test_fraction_lib_from_python3_raises_import_error_for_python2(self): + """testing if an ImportError will be raised and the error will be + handled gracefully under Python 2 when importing the Fraction library + which is introduced in Python 3. + + This is purely done for increasing the code coverage to 100% under + Python 3 + """ + import mock + import sys + with mock.patch.dict(sys.modules, {'fractions': None}): + # the coverage should be now 100% + tc = Timecode('24') diff --git a/timecode/__init__.py b/timecode/__init__.py index 1c167cc..de32754 100644 --- a/timecode/__init__.py +++ b/timecode/__init__.py @@ -20,18 +20,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import math -from decimal import Decimal, ROUND_HALF_UP -__version__ = '1.2.5' - - -def school_math_round(num): - """ - Try built in round(2.5) and then use this method instead... Why? Not sure, - but this is right, for the moment at least. - """ - return int(Decimal(num).quantize(Decimal(1), rounding=ROUND_HALF_UP)) +__version__ = '1.3.0' class Timecode(object): @@ -84,17 +74,17 @@ def __init__(self, framerate, start_timecode=None, start_seconds=None, # attribute override order # start_timecode > frames > start_seconds if start_timecode: - self._frames = self.tc_to_frames(start_timecode) + self.frames = self.tc_to_frames(start_timecode) else: - if frames is not None: # because 0==False, and frames can be 0 + if frames is not None: self.frames = frames elif start_seconds is not None: if start_seconds == 0: raise ValueError("``start_seconds`` argument can not be 0") - self._frames = self.float_to_tc(start_seconds) + self.frames = self.float_to_tc(start_seconds) else: # use default value of 00:00:00:00 - self._frames = self.tc_to_frames('00:00:00:00') + self.frames = self.tc_to_frames('00:00:00:00') @property def frames(self): @@ -182,7 +172,6 @@ def framerate(self, framerate): # lint:ok else: self.drop_frame = True elif any(map(lambda x: framerate.startswith(x), ['23.976', '23.98'])): - framerate = '24' self._int_framerate = 24 elif framerate in ['ms', '1000']: self._int_framerate = 1000 @@ -204,7 +193,7 @@ def set_fractional(self, state): def set_timecode(self, timecode): """Sets the frames by using the given timecode """ - self._frames = self.tc_to_frames(timecode) + self.frames = self.tc_to_frames(timecode) def float_to_tc(self, seconds): """set the frames by using the given seconds @@ -214,6 +203,10 @@ def float_to_tc(self, seconds): def tc_to_frames(self, timecode): """Converts the given timecode to frames """ + # timecode could be a Timecode instance + if isinstance(timecode, Timecode): + return timecode.frames + hours, minutes, seconds, frames = map(int, self.parse_timecode(timecode)) if isinstance(timecode, int): @@ -223,7 +216,10 @@ def tc_to_frames(self, timecode): if self.drop_frame: timecode = ';'.join(timecode.rsplit(':', 1)) - ffps = float(self._framerate) + if self.framerate != 'frames': + ffps = float(self.framerate) + else: + ffps = float(self._int_framerate) if self.drop_frame: # Number of drop frames is 6% of framerate rounded to nearest @@ -257,27 +253,14 @@ def tc_to_frames(self, timecode): (ifps * seconds) + frames) - \ (drop_frames * (total_minutes - (total_minutes // 10))) - # Special case correction for 29.97 NDF (Non-Dropframe) - if self._framerate in {'29.97', '59.94'} and self.drop_frame is False: - # Correct for using 30 fps calculation, by converting it to 29.97 - # or from 60 fps calculation, by converting it to 59.94 - # FIXME: Not sure what to do when new frame number is fractional. - # Currently we're just flooring the result, but not sure what we - # should do yet. TBD. - corrected_frame_number = frame_number * (ffps / float(ifps)) - frames = math.ceil(corrected_frame_number) + 1 - - else: - frames = frame_number + 1 - - return frames + return frame_number + 1 # frames def frames_to_tc(self, frames): """Converts frames back to timecode :returns str: the string representation of the current time code """ - ffps = float(self._framerate) + ffps = float(self.framerate) if self.drop_frame: # Number of frames to drop on the minute marks is the nearest @@ -298,10 +281,6 @@ def frames_to_tc(self, frames): frame_number = frames - 1 - if frame_number < 0: - # Negative time. Add 24 hours. - frame_number += frames_per_24_hours - # If frame_number is greater than 24 hrs, next operation will rollover # clock frame_number %= frames_per_24_hours @@ -329,20 +308,17 @@ def frames_to_tc(self, frames): def tc_to_string(self, hrs, mins, secs, frs): if self.fraction_frame: - return "{hh:02d}:{mm:02d}:{ss:06.3f}".format(hh=hrs, - mm=mins, - ss=secs + frs - ) + return "{hh:02d}:{mm:02d}:{ss:06.3f}".format( + hh=hrs, mm=mins, ss=secs + frs + ) ff = "%02d" if self.ms_frame: ff = "%03d" - return ("%02d:%02d:%02d%s" + ff) % (hrs, - mins, - secs, - self.frame_delimiter, - frs) + return ("%02d:%02d:%02d%s" + ff) % ( + hrs, mins, secs, self.frame_delimiter, frs + ) @classmethod def parse_timecode(cls, timecode): @@ -377,7 +353,7 @@ def frame_delimiter(self): return ':' def __iter__(self): - return self + yield self def next(self): self.add_frames(1) @@ -390,7 +366,7 @@ def back(self): def add_frames(self, frames): """adds or subtracts frames number of frames """ - self._frames += frames + self.frames += frames def sub_frames(self, frames): """adds or subtracts frames number of frames @@ -400,77 +376,72 @@ def sub_frames(self, frames): def mult_frames(self, frames): """multiply frames """ - self._frames *= frames + self.frames *= frames def div_frames(self, frames): """adds or subtracts frames number of frames""" - self._frames = self._frames / frames + self.frames = int(self.frames / frames) def __eq__(self, other): """the overridden equality operator """ if isinstance(other, Timecode): - return self._framerate == other._framerate and \ - self._frames == other._frames + return self.framerate == other.framerate and self.frames == other.frames elif isinstance(other, str): - new_tc = Timecode(self._framerate, other) + new_tc = Timecode(self.framerate, other) return self.__eq__(new_tc) elif isinstance(other, int): - return self._frames == other + return self.frames == other def __ge__(self, other): """override greater or equal to operator""" if isinstance(other, Timecode): - return self._framerate == other._framerate and \ - self._frames >= other._frames + return self.framerate == other.framerate and self.frames >= other.frames elif isinstance(other, str): - new_tc = Timecode(self._framerate, other) - return self._frames >= new_tc._frames + new_tc = Timecode(self.framerate, other) + return self.frames >= new_tc.frames elif isinstance(other, int): - return self._frames >= other + return self.frames >= other def __gt__(self, other): """override greater operator""" if isinstance(other, Timecode): - return self._framerate == other._framerate and \ - self._frames > other._frames + return self.framerate == other.framerate and self.frames > other.frames elif isinstance(other, str): - new_tc = Timecode(self._framerate, other) - return self._frames > new_tc._frames + new_tc = Timecode(self.framerate, other) + return self.frames > new_tc.frames elif isinstance(other, int): - return self._frames > other + return self.frames > other def __le__(self, other): """override less or equal to operator""" if isinstance(other, Timecode): - return self._framerate == other._framerate and \ - self._frames <= other._frames + return self.framerate == other.framerate and self.frames <= other.frames elif isinstance(other, str): - new_tc = Timecode(self._framerate, other) - return self._frames <= new_tc._frames + new_tc = Timecode(self.framerate, other) + return self.frames <= new_tc.frames elif isinstance(other, int): - return self._frames <= other + return self.frames <= other def __lt__(self, other): """override less operator""" if isinstance(other, Timecode): - return self._framerate == other._framerate and \ - self._frames < other._frames + return self.framerate == other.framerate and self.frames < other.frames elif isinstance(other, str): - new_tc = Timecode(self._framerate, other) - return self._frames < new_tc._frames + new_tc = Timecode(self.framerate, other) + return self.frames < new_tc.frames elif isinstance(other, int): - return self._frames < other + return self.frames < other def __add__(self, other): """returns new Timecode instance with the given timecode or frames added to this one """ # duplicate current one - tc = Timecode(self._framerate, frames=self._frames) + tc = Timecode(self.framerate, frames=self.frames) if isinstance(other, Timecode): - tc.add_frames(other._frames) + tc.add_frames(other.frames) elif isinstance(other, int): tc.add_frames(other) else: @@ -484,79 +455,83 @@ def __add__(self, other): def __sub__(self, other): """returns new Timecode instance with subtracted value""" if isinstance(other, Timecode): - subtracted_frames = self._frames - other._frames + subtracted_frames = self.frames - other.frames elif isinstance(other, int): - subtracted_frames = self._frames - other + subtracted_frames = self.frames - other else: raise TimecodeError( 'Type %s not supported for arithmetic.' % other.__class__.__name__ ) - return Timecode(self._framerate, frames=abs(subtracted_frames)) + return Timecode(self.framerate, frames=abs(subtracted_frames)) def __mul__(self, other): """returns new Timecode instance with multiplied value""" if isinstance(other, Timecode): - multiplied_frames = self._frames * other._frames + multiplied_frames = self.frames * other.frames elif isinstance(other, int): - multiplied_frames = self._frames * other + multiplied_frames = self.frames * other else: raise TimecodeError( 'Type %s not supported for arithmetic.' % other.__class__.__name__ ) - return Timecode(self._framerate, frames=multiplied_frames) + return Timecode(self.framerate, frames=multiplied_frames) def __div__(self, other): """returns new Timecode instance with divided value""" if isinstance(other, Timecode): - div_frames = self._frames / other._frames + div_frames = int(float(self.frames) / float(other.frames)) elif isinstance(other, int): - div_frames = self._frames / other + div_frames = int(float(self.frames) / float(other)) else: raise TimecodeError( 'Type %s not supported for arithmetic.' % other.__class__.__name__ ) - return Timecode(self._framerate, frames=div_frames) + return Timecode(self.framerate, frames=div_frames) + + def __truediv__(self, other): + """returns new Timecode instance with divided value""" + return self.__div__(other) def __repr__(self): - return self.tc_to_string(*self.frames_to_tc(self._frames)) + return self.tc_to_string(*self.frames_to_tc(self.frames)) @property def hrs(self): - hrs, mins, secs, frs = self.frames_to_tc(self._frames) + hrs, mins, secs, frs = self.frames_to_tc(self.frames) return hrs @property def mins(self): - hrs, mins, secs, frs = self.frames_to_tc(self._frames) + hrs, mins, secs, frs = self.frames_to_tc(self.frames) return mins @property def secs(self): - hrs, mins, secs, frs = self.frames_to_tc(self._frames) + hrs, mins, secs, frs = self.frames_to_tc(self.frames) return secs @property def frs(self): - hrs, mins, secs, frs = self.frames_to_tc(self._frames) + hrs, mins, secs, frs = self.frames_to_tc(self.frames) return frs @property def frame_number(self): """returns the 0 based frame number of the current timecode instance """ - return self._frames - 1 + return self.frames - 1 @property def float(self): """returns the seconds as float """ - return float(self._frames) / float(self._int_framerate) + return float(self.frames) / float(self._int_framerate) class TimecodeError(Exception):