import os import os.path import tempfile import threading import unittest import mock from lvc import video import base class GetMediaInfoTest(base.Test): def assertClose(self, output, expected): diff = output - expected self.assertTrue(diff ** 2 < 0.04, # abs(diff) < 0.2 "%s != %s" % (output, expected)) def assertEqualOutput(self, filename, expected): full_path = os.path.join(self.testdata_dir, filename) try: output = video.get_media_info(full_path) except Exception, e: raise AssertionError( 'Error parsing %r\nException: %r\nOutput: %s' % ( filename, e, video.get_ffmpeg_output(full_path))) duration_output = output.pop('duration', None) duration_expected = expected.pop('duration', None) if duration_output is not None and duration_expected is not None: self.assertClose(duration_output, duration_expected) else: # put them back in, let assertEqual handle the difference output['duration'] = duration_output expected['duration'] = duration_expected self.assertEqual(output, expected) def test_mp3_0(self): self.assertEqualOutput('mp3-0.mp3', {'container': 'mp3', 'audio_codec': 'mp3', 'title': 'Invisible Walls', 'artist': 'Revolution Void', 'album': 'Increase The Dosage', 'track': '1', 'genre': 'Blues', 'duration': 1.07}) def test_mp3_1(self): self.assertEqualOutput('mp3-1.mp3', {'container': 'mp3', 'audio_codec': 'mp3', 'title': 'Race Lieu', 'artist': 'Ckz', 'album': 'The Heart EP', 'track': '2/5', 'duration': 1.07}) def test_mp3_2(self): self.assertEqualOutput('mp3-2.mp3', {'container': 'mp3', 'audio_codec': 'mp3', 'artist': 'This American Life', 'genre': 'Podcast', 'title': '#426: Tough Room 2011', 'duration': 1.09}) def test_theora(self): self.assertEqualOutput('theora.ogv', {'container': 'ogg', 'video_codec': 'theora', 'audio_codec': 'vorbis', 'width': 400, 'height': 304, 'duration': 5.0}) def test_theora_with_ogg_extension(self): self.assertEqualOutput('theora_with_ogg_extension.ogg', {'container': 'ogg', 'video_codec': 'theora', 'width': 320, 'height': 240, 'duration': 0.1}) def test_webm_0(self): self.assertEqualOutput('webm-0.webm', {'container': ['matroska', 'webm'], 'video_codec': 'vp8', 'width': 1920, 'height': 912, 'duration': 0.43}) def test_mp4_0(self): self.assertEqualOutput('mp4-0.mp4', {'container': ['mov', 'mp4', 'm4a', '3gp', '3g2', 'mj2', 'isom', 'mp41'], 'video_codec': 'h264', 'audio_codec': 'aac', 'width': 640, 'height': 480, 'title': 'Africa: Cash for Climate Change?', 'duration': 312.37}) def test_nuls(self): self.assertEqualOutput('nuls.mp3', {'container': 'mp3', 'title': 'Invisible'}) @unittest.skip('inconsistent parsing of DRMed files') def test_drm(self): self.assertEqualOutput('drm.m4v', {'container': ['mov', 'mp4', 'm4a', '3gp', '3g2', 'mj2', 'M4V', 'M4V ', 'mp42', 'isom'], 'video_codec': 'none', 'audio_codec': 'aac', 'has_drm': ['audio', 'video'], 'width': 640, 'height': 480, 'title': 'Thinkers', 'artist': 'The Most Extreme', 'album': 'The Most Extreme', 'track': '10', 'genre': 'Nonfiction', 'duration': 2668.8}) class GetThumbnailTest(base.Test): def setUp(self): base.Test.setUp(self) self.video_path = os.path.join(self.testdata_dir, 'theora.ogv') self.temp_path = tempfile.NamedTemporaryFile( suffix='.png') def generate_thumbnail(self, width, height): completion = mock.Mock() with mock.patch('lvc.video.idle_add') as mock_idle_add: with mock.patch('threading.Thread') as mock_thread: video.get_thumbnail(self.video_path, width, height, self.temp_path.name, completion, skip=0) # get_thumbnail() creates a thread to create the thumbnail. # Run the function for that thread now. mock_thread.call_args[1]['target']() self.assertEquals(mock_idle_add.call_count, 1) # At the end of the thread it uses add_idle() to call the # completion function. Run that now. mock_idle_add.call_args[0][0]() # Now when we call get_thumbnail() it should return # immediately with the thumbnail path = completion.call_args[0][0] self.assertNotEquals(path, None) return video.VideoFile(path) def test_original_size(self): thumbnail = self.generate_thumbnail(-1, -1) self.assertEqual(thumbnail.width, 400) self.assertEqual(thumbnail.height, 304) def test_height_resize(self): thumbnail = self.generate_thumbnail(200, -1) self.assertEqual(thumbnail.width, 200) self.assertEqual(thumbnail.height, 152) def test_width_resize(self): thumbnail = self.generate_thumbnail(-1, 152) self.assertEqual(thumbnail.width, 200) self.assertEqual(thumbnail.height, 152) def test_both_resize(self): thumbnail = self.generate_thumbnail(100, 100) self.assertEqual(thumbnail.width, 100) self.assertEqual(thumbnail.height, 100) class VideoFileTest(base.Test): def setUp(self): base.Test.setUp(self) self.video_path = os.path.join(self.testdata_dir, 'theora.ogv') self.video = video.VideoFile(self.video_path) self.video.thumbnails = {} def get_thumbnail_from_video(self, **kwargs): """Run Video.get_thumbnail() This method uses mock to intercept the threading and idle_add calls and just runs the code in the current thread """ completion = mock.Mock() with mock.patch('lvc.video.idle_add') as mock_idle_add: with mock.patch('threading.Thread') as mock_thread: initial_rv = self.video.get_thumbnail(completion, **kwargs) if initial_rv is not None: # we already had a thumbnail and didn't have to do # anything synchrously return video.VideoFile(initial_rv) # We don't already have a thumbnail, so get_thumbnail() # created a thread to create it. Run the function for that # thread. mock_thread.call_args[1]['target']() self.assertEquals(mock_idle_add.call_count, 1) # At the end of the thread it uses add_idle() to call the # completion function. Run that now. mock_idle_add.call_args[0][0]() # Now when we call get_thumbnail() it should return # immediately with the thumbnail path = self.video.get_thumbnail(completion, **kwargs) self.assertNotEquals(path, None) return video.VideoFile(path) def test_get_thumbnail_original_size(self): thumbnail = self.get_thumbnail_from_video() self.assertEqual(thumbnail.width, 400) self.assertEqual(thumbnail.height, 304) def test_get_thumbnail_scaled_width(self): thumbnail = self.get_thumbnail_from_video(width=200) self.assertEqual(thumbnail.width, 200) self.assertEqual(thumbnail.height, 152) def test_get_thumbnail_scaled_height(self): thumbnail = self.get_thumbnail_from_video(height=152) self.assertEqual(thumbnail.width, 200) self.assertEqual(thumbnail.height, 152) def test_get_thumbnail_scaled_both(self): thumbnail = self.get_thumbnail_from_video(width=100, height=100) self.assertEqual(thumbnail.width, 100) self.assertEqual(thumbnail.height, 100) def test_get_thumbnail_cache(self): thumbnail = self.get_thumbnail_from_video() thumbnail2 = self.get_thumbnail_from_video() self.assertEqual(thumbnail.filename, thumbnail2.filename) def test_get_thumbnail_audio(self): audio_path = os.path.join(self.testdata_dir, 'mp3-0.mp3') audio = video.VideoFile(audio_path) def complete(): pass self.assertEqual(audio.get_thumbnail(complete), None) self.assertEqual(audio.get_thumbnail(complete, 90, 70), None)