def test_form_valid__scraped__embed(self): """ Checks that, if the form represents a scraped video with embed code, the success_url is the url of the scraped_video view, and that the session data contains the scraped video as 'video' and the video's url as 'url'. """ scraped_url = reverse('localtv_submit_scraped_video') view = SubmitURLView(scraped_url=scraped_url) video_url = "http://google.com" url = "%s?url=%s" % (reverse('localtv_submit_video'), video_url) view.request = self.factory.get(url) expected_success_url = "%s?%s" % (scraped_url, view.request.GET.urlencode()) form = view.get_form(view.get_form_class()) video = VidscraperVideo(video_url) video.embed_code = "blink" form.video_cache = video form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], { 'video': video, 'url': video_url }) self.assertEqual(view.success_url, expected_success_url)
def test_items(self): video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") # Make sure items can be iterated over and that there's one # for every field. for i, item in enumerate(video.items()): self.assertEqual(item[0], Video._all_fields[i])
def test_form_valid__scraped__embed(self): """ Checks that, if the form represents a scraped video with embed code, the success_url is the url of the scraped_video view, and that the session data contains the scraped video as 'video' and the video's url as 'url'. """ scraped_url = reverse('localtv_submit_scraped_video') view = SubmitURLView(scraped_url=scraped_url) video_url = "http://google.com" url = "%s?url=%s" % (reverse('localtv_submit_video'), video_url) view.request = self.factory.get(url) expected_success_url = "%s?%s" % (scraped_url, view.request.GET.urlencode()) form = view.get_form(view.get_form_class()) video = VidscraperVideo(video_url) video.embed_code = "blink" form.video_cache = video form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], {'video': video, 'url': video_url}) self.assertEqual(view.success_url, expected_success_url)
def _test_submit__succeed(self, url, next_view, **kwargs): """ A GET request to the SubmitURLView with GET data should submit the form if the GET data overlaps with the form field(s). On success, the user should be redirected to the correct submission completion view, preserving GET data. All the views to which the user could be redirected are instances of the same class-based view. This is essentially a test for backwards-compatibility. """ data = {'url': url, 'q': 'hello', 'next': 'blink'} expected_url = "%s?%s" % ( reverse(next_view), urlencode(data) ) video = VidscraperVideo(url) video._loaded = True for attr, value in kwargs.iteritems(): setattr(video, attr, value) with patch.object(vidscraper, 'auto_scrape', return_value=video): response = self.client.get(self.url, data) self.assertRedirects(response, expected_url)
def test_items_with_fields(self): fields = ['title', 'user'] video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4", fields=fields) # Make sure items can be iterated over and that there's one # for every field. for i, item in enumerate(video.items()): self.assertEqual(item[0], fields[i])
def test_form_valid__embedrequest(self): """ Checks that, if the form represents an embedrequest video - i.e. a video that vidscraper can't scrape and which doesn't look like a direct link - the success_url is set to the url of the embedrequest view, and that the session data contains the scraped video (or ``None``) as 'video' and the video's url as 'url'. """ embed_url = reverse('localtv_submit_embedrequest_video') view = SubmitURLView(embed_url=embed_url) video_url = 'http://google.com' url = "%s?url=%s" % (reverse('localtv_submit_video'), video_url) view.request = self.factory.get(url) expected_success_url = "%s?%s" % (embed_url, view.request.GET.urlencode()) form = view.get_form(view.get_form_class()) # Option 1: no video form.video_cache = None form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], { 'video': None, 'url': video_url }) self.assertEqual(view.success_url, expected_success_url) # Option two: video missing embed & file_url video = VidscraperVideo(video_url) form.video_cache = video form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], { 'video': video, 'url': video_url }) self.assertEqual(view.success_url, expected_success_url) # Option three: video with expiring file_url. video.file_url = 'hola' video.file_url_expires = datetime.datetime.now() + datetime.timedelta( 1) view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], { 'video': video, 'url': video_url }) self.assertEqual(view.success_url, expected_success_url)
def test_serialize__json(self): """ Tests that serialized videos can be transformed to and from json. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") # we load the video data this way to avoid depending on the network video_data = CARAMELL_DANSEN_API_DATA.copy() video_data['tags'] = list(video_data['tags']) video._apply(video_data) data = video.serialize() new_data = json.loads(json.dumps(data)) self.assertEqual(new_data, data)
def test_serialize__pickle(self): """ Tests that serialized videos can be pickled and unpickled. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") # we load the video data this way to avoid depending on the network video_data = CARAMELL_DANSEN_API_DATA.copy() video_data['tags'] = list(video_data['tags']) video._apply(video_data) data = video.serialize() new_data = pickle.loads(pickle.dumps(data, pickle.HIGHEST_PROTOCOL)) self.assertEqual(new_data, data)
def get_video(self, url, fields=None, api_keys=None, require_loaders=True): """ For each registered :mod:`suite <vidscraper.suites>`, calls :meth:`~BaseSuite.get_video` with the given ``url``, ``fields``, and ``api_keys``, until a suite returns a :class:`.Video` instance. :param require_loaders: Changes the behavior if no suite is found which handles the given parameters. If ``True`` (default), :exc:`.UnhandledVideo` will be raised; otherwise, a video will returned with the given ``url`` and ``fields``, but it will not be able to load any additional data. :raises: :exc:`.UnhandledVideo` if ``require_loaders`` is ``True`` and no registered suite returns a video for the given parameters. :returns: :class:`.Video` instance with no data loaded. """ for suite in self.suites: try: return suite.get_video(url, fields=fields, api_keys=api_keys) except UnhandledVideo: pass if require_loaders: raise UnhandledVideo(url) return Video(url, fields=fields)
def test_get_file__open(self): """ Tests that open video formats are preferred over proprietary. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") file1 = VideoFile(url='http://google.com', mime_type="video/ogg") file2 = VideoFile(url='http://xkcd.com', mime_type="application/x-shockwave-flash") file3 = VideoFile(url='http://example.com', mime_type="video/mp4") video.files = [file1, file2, file3] self.assertEqual(video.get_file(), file1) video.files = [file3, file2, file1] self.assertEqual(video.get_file(), file1)
def test_form_valid__embedrequest(self): """ Checks that, if the form represents an embedrequest video - i.e. a video that vidscraper can't scrape and which doesn't look like a direct link - the success_url is set to the url of the embedrequest view, and that the session data contains the scraped video (or ``None``) as 'video' and the video's url as 'url'. """ embed_url = reverse('localtv_submit_embedrequest_video') view = SubmitURLView(embed_url=embed_url) video_url = 'http://google.com' url = "%s?url=%s" % (reverse('localtv_submit_video'), video_url) view.request = self.factory.get(url) expected_success_url = "%s?%s" % (embed_url, view.request.GET.urlencode()) form = view.get_form(view.get_form_class()) # Option 1: no video form.video_cache = None form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], {'video': None, 'url': video_url}) self.assertEqual(view.success_url, expected_success_url) # Option two: video missing embed & file_url video = VidscraperVideo(video_url) form.video_cache = video form.cleaned_data = {'url': video_url} view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], {'video': video, 'url': video_url}) self.assertEqual(view.success_url, expected_success_url) # Option three: video with expiring file_url. video.file_url = 'hola' video.file_url_expires = datetime.datetime.now() + datetime.timedelta(1) view.form_valid(form) self.assertEqual(view.request.session[view.get_session_key()], {'video': video, 'url': video_url}) self.assertEqual(view.success_url, expected_success_url)
def video_from_vidscraper_video(video_dict, site_pk, import_app_label=None, import_model=None, import_pk=None, status=None, author_pks=None, category_pks=None, clear_rejected=False): vidscraper_video = VidscraperVideo.deserialize(video_dict, API_KEYS) import_class = get_model(import_app_label, import_model) try: source_import = import_class.objects.get( pk=import_pk, status=import_class.STARTED) except import_class.DoesNotExist: logging.warn('Retrying %r: expected %s instance (pk=%r) missing.', vidscraper_video.url, import_class.__name__, import_pk) video_from_vidscraper_video.retry() try: try: vidscraper_video.load() except Exception: source_import.handle_error( ('Skipped %r: Could not load video data.' % vidscraper_video.url), is_skip=True, with_exception=True) return if category_pks: categories = Category.objects.filter(pk__in=category_pks) else: categories = None if author_pks: authors = User.objects.filter(pk__in=author_pks) else: authors = None video = Video.from_vidscraper_video(vidscraper_video, status=status, source_import=source_import, authors=authors, categories=categories, site_pk=site_pk, commit=False, update_index=False) try: video.clean_fields() # If clear_rejected is True, we've already deleted any rejected # videos, so there's no need to explicitly exclude them. # If clear_rejected is False, this is not the first run, and # so rejected videos need to not be excluded in this check. video._check_for_duplicates(exclude_rejected=False) video.validate_unique() except ValidationError, e: source_import.handle_error(("Skipping %r: %r" % ( vidscraper_video.url, e.message)), is_skip=True) return else:
def test_m2m_errors(self): """ If video.save_m2m raises an exception during import, the video should be deleted and the error reraised. """ class FakeException(Exception): pass video = mock.MagicMock(save_m2m=mock.MagicMock( side_effect=FakeException)) kwargs = {'from_vidscraper_video.return_value': video} vidscraper_video = VidscraperVideo(None) with mock.patch('localtv.tasks.get_model'): with mock.patch('localtv.tasks.Video', **kwargs): with self.assertRaises(FakeException): video_from_vidscraper_video.apply(args=(vidscraper_video.serialize(), 1)) video.save.assert_called_once_with(update_index=False) video.save_m2m.assert_called_once_with() video.delete.assert_called_once_with()
def _test_submit__succeed(self, url, next_view, **kwargs): """ A GET request to the SubmitURLView with GET data should submit the form if the GET data overlaps with the form field(s). On success, the user should be redirected to the correct submission completion view, preserving GET data. All the views to which the user could be redirected are instances of the same class-based view. This is essentially a test for backwards-compatibility. """ data = {'url': url, 'q': 'hello', 'next': 'blink'} expected_url = "%s?%s" % (reverse(next_view), urlencode(data)) video = VidscraperVideo(url) video._loaded = True for attr, value in kwargs.iteritems(): setattr(video, attr, value) with patch.object(vidscraper, 'auto_scrape', return_value=video): response = self.client.get(self.url, data) self.assertRedirects(response, expected_url)
def test_serialize__files(self): """ Tests that a video with associated files can still be serialized and deserialized. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") now = datetime.datetime.now() video.files = [VideoFile(url='http://google.com', expires=now, length=100, width=50, height=50, mime_type="video/x-flv"), VideoFile(url='http://xkcd.com', expires=now, length=75, width=80, height=80, mime_type="application/x-shockwave-flash"),] data = video.serialize() # verify that the data we expect is in the serialized version. self.assertEqual(data['files'][0]['url'], "http://google.com") self.assertEqual(data['files'][1]['mime_type'], "application/x-shockwave-flash") self.assertEqual(data['files'][0]['expires'], now.isoformat()) # Verify that the data can be deserialized as a video. new_video = Video.deserialize(data) self.assertEqual(dict(video.items()), dict(new_video.items()))
def test_m2m_errors(self): """ If video.save_m2m raises an exception during import, the video should be deleted and the error reraised. """ class FakeException(Exception): pass video = mock.MagicMock(save_m2m=mock.MagicMock( side_effect=FakeException)) kwargs = {'from_vidscraper_video.return_value': video} vidscraper_video = VidscraperVideo(None) with mock.patch('localtv.tasks.get_model'): with mock.patch('localtv.tasks.Video', **kwargs): with self.assertRaises(FakeException): video_from_vidscraper_video.apply( args=(vidscraper_video.serialize(), 1)) video.save.assert_called_once_with(update_index=False) video.save_m2m.assert_called_once_with() video.delete.assert_called_once_with()
def test_get_file__none(self): """ If there are no files, get_file() should return None. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") video.files = None self.assertTrue(video.get_file() is None) video.files = [] self.assertTrue(video.get_file() is None)
def test_serialize(self): video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") # we load the video data this way to avoid depending on the network video_data = CARAMELL_DANSEN_API_DATA.copy() video_data['tags'] = list(video_data['tags']) video._apply(video_data) data = video.serialize() # verify that the data we expect is in the serialized version. self.assertEqual(data['url'], video.url) self.assertEqual(data['title'], video.title) self.assertEqual(data['publish_datetime'], video.publish_datetime.isoformat()) # Verify that the data can be deserialized as a video. new_video = Video.deserialize(data) self.assertEqual(video.url, new_video.url) self.assertEqual(dict(video.items()), dict(new_video.items())) # Verify that data can be deserialized more than once. # (i.e. that it is not modified in-place during deserialization.) Video.deserialize(data)
def test_get_file__no_mimetypes(self): """ If none of the videos have mime types, the first file should be returned. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") file1 = VideoFile(url='http://google.com') file2 = VideoFile(url='http://xkcd.com') file3 = VideoFile(url='http://example.com') video.files = [file1, file2, file3] self.assertEqual(video.get_file(), file1) video.files = [file3, file2, file1] self.assertEqual(video.get_file(), file3)
def test_serialize(self): video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4") # we load the video data this way to avoid depending on the network video_data = CARAMELL_DANSEN_API_DATA.copy() video_data['tags'] = list(video_data['tags']) video._apply(video_data) data = video.serialize() # verify that the data we expect is in the serialized version. self.assertEqual(data['url'], video.url) self.assertEqual(data['title'], video.title) self.assertEqual(data['publish_datetime'], video.publish_datetime.isoformat()) # Verify that the data can be deserialized as a video. new_video = Video.deserialize(data) self.assertEqual(video.url, new_video.url) self.assertEqual(dict(video.items()), dict(new_video.items()))
def test_serialize__partial(self): """ Tests that a video with only some fields can still be serialized and deserialized. """ video = Video("http://www.youtube.com/watch?v=J_DV9b0x7v4", fields=('title', 'embed_code')) # we load the video data this way to avoid depending on the network video_data = CARAMELL_DANSEN_API_DATA.copy() video._apply(video_data) data = video.serialize() # verify that the data we expect is in the serialized version. self.assertEqual(data['url'], video.url) self.assertEqual(data['title'], video.title) self.assertEqual(data['embed_code'], video.embed_code) # Verify that the data can be deserialized as a video. new_video = Video.deserialize(data) self.assertEqual(video.url, new_video.url) self.assertEqual(dict(video.items()), dict(new_video.items()))
def get_video(self, url, fields=None, api_keys=None): """ Returns a video using this suite's loaders. This instance will not have data loaded. :param url: A video URL. Video website URLs generally work; more obscure urls (like API urls) might work as well. :param fields: A list of fields to be fetched for the video. Limiting this may decrease the number of HTTP requests required for loading the video. .. seealso:: :ref:`video-fields` :param api_keys: A dictionary of API keys for various services. Check the documentation for each :mod:`suite <vidscraper.suites>` to find what API keys they may want or require. :raises: :exc:`.UnhandledVideo` if none of this suite's loaders can handle the given url and api keys. """ # Sanity-check the url. if not url: raise UnhandledVideo(url) loaders = [] for cls in self.loader_classes: try: loader = cls(url, api_keys=api_keys) except UnhandledVideo: continue loaders.append(loader) if not loaders: raise UnhandledVideo(url) return Video(url, loaders=loaders, fields=fields)