async def _delete_files(self, podcast: Podcast, episodes: List[Episode]): podcast_file_names = {episode.file_name for episode in episodes} same_file_episodes = await self.request.app.objects.execute( Episode.select().where( Episode.podcast_id != podcast.id, Episode.file_name.in_(podcast_file_names), )) exist_file_names = { episode.file_name for episode in same_file_episodes or [] } files_to_remove = podcast_file_names - exist_file_names files_to_skip = exist_file_names & podcast_file_names if files_to_skip: self.logger.warning( "There are another episodes with files %s. Skip this files removing.", files_to_skip, ) storage = StorageS3() await storage.delete_files_async(list(files_to_remove)) await storage.delete_files_async( [f"{podcast.publish_id}.xml"], remote_path=settings.S3_BUCKET_RSS_PATH)
def test_download_sound__episode_downloaded__file_correct__ignore_downloading__ok( generate_rss_mock, db_objects, podcast, episode_data, mocked_youtube: MockYoutube, mocked_s3: MockS3Client, ): new_episode_data = { **episode_data, **{ "status": "published", "source_id": mocked_youtube.video_id, "watch_url": mocked_youtube.watch_url, "file_size": 1024, }, } episode: Episode = Episode.create(**new_episode_data) mocked_s3.get_file_size.return_value = episode.file_size generate_rss_mock.return_value = f"file_{episode.source_id}.mp3" result = download_episode(episode.watch_url, episode.id) with db_objects.allow_sync(): updated_episode: Episode = Episode.select().where( Episode.id == episode.id).first() generate_rss_mock.assert_called_with(episode.podcast_id) assert result == EPISODE_DOWNLOADING_IGNORED assert not mocked_youtube.download.called assert updated_episode.status == "published" assert updated_episode.published_at == updated_episode.created_at
def test_download_sound__youtube_exception__download_rollback( download_audio_mock, db_objects, podcast, episode_data, mocked_youtube: MockYoutube, mocked_s3: MockS3Client, ): new_episode_data = { **episode_data, **{ "status": "new", "source_id": mocked_youtube.video_id, "watch_url": mocked_youtube.watch_url, "file_size": 1024, }, } episode: Episode = Episode.create(**new_episode_data) download_audio_mock.side_effect = YoutubeException( "Youtube video is not available") result = download_episode(episode.watch_url, episode.id) with db_objects.allow_sync(): updated_episode: Episode = Episode.select().where( Episode.id == episode.id).first() download_audio_mock.assert_called_with(episode.watch_url, episode.file_name) assert result == EPISODE_DOWNLOADING_ERROR assert updated_episode.status == "new" assert updated_episode.published_at is None
def _update_all_rss(source_id: str): """ Allows to regenerate rss for all podcasts with requested episode (by source_id) """ logger.info( "Episodes with source #%s: updating rss for all podcasts included for", source_id, ) affected_episodes = list( Episode.select(Episode.podcast).where(Episode.source_id == source_id)) podcast_ids = [episode.podcast_id for episode in affected_episodes] logger.info("Found podcasts for rss updates: %s", podcast_ids) for podcast_id in podcast_ids: generate_rss(podcast_id)
async def _delete_file(self, episode: Episode): """ Removing file associated with requested episode """ same_file_episodes = await self.request.app.objects.execute( Episode.select().where( Episode.source_id == episode.source_id, Episode.status != Episode.STATUS_NEW, Episode.id != episode.id, )) if same_file_episodes: episode_ids = ",".join( [f"#{episode.id}" for episode in same_file_episodes]) self.logger.warning( f"There are another episodes for file {episode.file_name}: {episode_ids}. " f"Skip file removing.") return return await StorageS3().delete_files_async([episode.file_name])
def test_download_sound__episode_new__correct_downloading( download_audio_mock, generate_rss_mock, db_objects, podcast, episode_data, mocked_youtube: MockYoutube, mocked_s3: MockS3Client, mocked_ffmpeg: Mock, ): new_episode_data = { **episode_data, **{ "status": "new", "source_id": mocked_youtube.video_id, "watch_url": mocked_youtube.watch_url, "file_size": 1024, }, } episode: Episode = Episode.create(**new_episode_data) download_audio_mock.return_value = episode.file_name generate_rss_mock.return_value = f"file_{episode.source_id}.mp3" result = download_episode(episode.watch_url, episode.id) with db_objects.allow_sync(): updated_episode: Episode = Episode.select().where( Episode.id == episode.id).first() generate_rss_mock.assert_called_with(episode.podcast_id) download_audio_mock.assert_called_with(episode.watch_url, episode.file_name) mocked_ffmpeg.assert_called_with(episode.file_name) assert result == EPISODE_DOWNLOADING_OK assert updated_episode.status == "published" assert updated_episode.published_at == updated_episode.created_at
async def post(self): podcast_id = int(self.request.match_info.get("podcast_id")) podcast: Podcast = await self._get_object() cleaned_data = await self._validate() youtube_link = cleaned_data["youtube_link"].strip() video_id = get_video_id(youtube_link) if not video_id: add_message(self.request, f"YouTube link is not correct: {youtube_link}") return redirect(self.request, "podcast_details", podcast_id=podcast_id) same_episodes: Iterable[ Episode] = await self.request.app.objects.execute( Episode.select().where(Episode.source_id == video_id).order_by( Episode.created_at.desc())) episode_in_podcast, last_same_episode = None, None for episode in same_episodes: last_same_episode = last_same_episode or episode if episode.podcast_id == podcast_id: episode_in_podcast = episode break if episode_in_podcast: self.logger.info( f"Episode for video [{video_id}] already exists for current " f"podcast {podcast_id}. Redirecting to {episode_in_podcast}..." ) add_message(self.request, "Episode already exists in podcast.") return redirect( self.request, "episode_details", podcast_id=podcast_id, episode_id=episode_in_podcast.id, ) try: episode_data = await self._get_episode_data( same_episode=last_same_episode, podcast_id=podcast_id, video_id=video_id, youtube_link=youtube_link, ) except YoutubeFetchError: return redirect(self.request, "podcast_details", podcast_id=podcast_id) episode = await self.request.app.objects.create( Episode, **episode_data) if podcast.download_automatically: episode.status = Episode.STATUS_DOWNLOADING await self.request.app.objects.update(episode) await self._enqueue_task( tasks.download_episode, youtube_link=episode.watch_url, episode_id=episode.id, ) add_message( self.request, f"Downloading for youtube {episode.source_id} was started.", ) if is_mobile_app(self.request): return redirect(self.request, "progress") return redirect( self.request, "episode_details", podcast_id=podcast_id, episode_id=str(episode.id), )