def subscribe(): form = SubscribeForm() if form.validate_on_submit(): videos = get_channel_videos(form.channel.data) if not videos: raise DownloadError('Channel \'{}\' not found'.format( form.channel.data)) channel = Channel(name=videos[0]['uploader_id'], channel_id=videos[0]['channel_id']) db.session.add(channel) db.session.commit() for v in videos: if v and 'id' in v: video = Video(video_id=v['id'], title=v['title'], channel_id=channel.id, description=v['description'], thumbnail_url=v['thumbnail'], duration=v['duration'], upload_date=datetime.strptime( v['upload_date'], '%Y%m%d'), view_count=v['view_count']) db.session.add(video) db.session.commit() return redirect('/channels')
def _extract_songs(self, ydl: youtube_dl.YoutubeDL, url: str): info = ydl.extract_info(url, download=False) if not info: raise DownloadError('Data could not be retrieved') if '_type' in info and info['_type'] == 'playlist': entries = info['entries'] else: entries = [info] results = [(e['title'], e['url']) for e in entries] return results
def get_meta_info(url): """ Get metadata info from YouTube video. :param url: YouTube video URL """ opts = { 'format': 'bestaudio/best', 'forcefilename': True, 'noplaylist': True } # Try up to 3 times, as youtubedl tends to be flakey for _ in range(settings.YOUTUBE_MAX_RETRIES): try: with YoutubeDL(opts) as ydl: info = ydl.extract_info(url, download=False) filename = ydl.prepare_filename(info) parsed_artist = '' parsed_title = '' # Use youtube_title_parse library to attempt to parse the YouTube video title into # the track's artist and title. result = get_artist_title(info['title']) if result: parsed_artist, parsed_title = result metadata = { # YT video title 'title': info['title'], # YT video uploader 'uploader': info['uploader'], # YT video's embedded track artist (some official songs) 'embedded_artist': info['artist'], # YT video's embedded track title (some official songs) 'embedded_title': info['track'], # Artist name parsed from the YouTube video title 'parsed_artist': parsed_artist, # Title parsed from the YouTube video title 'parsed_title': parsed_title, # Duration of YouTube video in seconds 'duration': info['duration'], # YouTube video URL 'url': info['webpage_url'], # Filename (including extension) 'filename': filename } return metadata except DownloadError: # Allow for retry pass raise DownloadError('Unable to parse YouTube link')
def serve_mp3_file(videoid): def set_downloaded_flag(videoid): video = Video.query.filter_by(video_id=videoid).first_or_404() video.is_downloaded = True db.session.commit() return try: if download_mp3(videoid) == 0: set_downloaded_flag(videoid) return send_file('static/{}.mp3'.format(videoid), attachment_filename='{}.mp3'.format(videoid)) except: raise DownloadError( 'Could not download audio for video ID \'{}\''.format(videoid)) finally: try: os.remove('app/static/{}.mp3'.format(videoid)) print('Deleted file \'{}.mp3\''.format(videoid)) except OSError: raise DownloadError( 'Could not download audio for video ID \'{}\''.format(videoid))
def unsubscribe(): form = UnsubscribeForm() # if form.validate_on_submit(): channel = Channel.query.get(form.channel.data) if not channel: raise DownloadError('Channel \'{}\' not found'.format( form.channel.data)) for video in Video.query.with_parent(channel).all(): db.session.delete(video) db.session.delete(channel) db.session.commit() return redirect('/channels')
def test_download_episode__downloading_failed__roll_back_changes__ok( self, episode, mocked_youtube, mocked_ffmpeg, mocked_s3, mocked_generate_rss_task, dbs): file_path = settings.TMP_AUDIO_PATH / episode.file_name with open(file_path, "wb") as file: file.write(b"EpisodeData") mocked_youtube.download.side_effect = DownloadError( "Video is not available") result = await_(DownloadEpisodeTask(db_session=dbs).run(episode.id)) episode = await_(Episode.async_get(dbs, id=episode.id)) mocked_youtube.download.assert_called_with([episode.watch_url]) mocked_s3.upload_file.assert_not_called() mocked_generate_rss_task.run.assert_not_called() assert result == FinishCode.ERROR assert episode.status == Episode.Status.ERROR assert episode.published_at is None
def play_url(): # this only plays http urls for now, torrents soon. global title url = request.args.get('url') # grab url from /play?url=* if not url.startswith('http'): # in case the user forgot it log('url missing http/wrong protocol') url = 'http://' + url # let's assume it's http, not https log('received url %s' % url) log('requesting headers from %s...' % url) req = Request(url) req.get_method = lambda: 'HEAD' # only request headers, no content response = urlopen(req) ctype = response.headers['content-type'] ctype_split = ctype.split('/') # split into 2 parts log('headers received. content type is %s' % ctype) try: if ctype_split[0] == 'audio' or ctype_split[0] == 'video': log('url was raw media file, playing! :)') title = url # i guess this works? :T play_omxplayer(url) elif ctype_split[1] == 'x-bittorrent': log('loading torrents not implemented.') # this isn't implemented yet. elif ctype_split[0] == 'text': # here we check if it's a livestream, and if so get the RTMP url log('checking if url is a livestream...') live = Livestreamer() try: if "youtube" in url: raise RuntimeError( "youtube is f****d up w/ streaming, falling back to youtube-dl" ) plugin = live.resolve_url(url) streams = plugin.get_streams() stream = streams.get( "best") # fingers crossed for best quality stream_url_types = ['rtmp', 'url' ] # things that livestreamer can have :D for stream_type in stream_url_types: if hasattr(stream, stream_type): log('url is livestream!') title = "%s (livestream)" % url play_omxplayer(getattr(stream, stream_type)) return '', 204 except (PluginError, RuntimeError ) as e: # therefore url is not (supported) livestream pass # continue and let youtube-dl try. log('loading youtube-dl for further processing') ydl = YoutubeDL({ 'outtmpl': '%(id)s%(ext)s', 'restrictfilenames': True }) ydl.add_default_info_extractors() result = ydl.extract_info(url, download=False) if 'entries' in result: # if video is a playlist video = result['entries'][ 0] # play the 1st video in the playlist else: video = result play_omxplayer(video['url']) title = video['title'] else: raise DownloadError('Invalid filetype: not audio, video, or text.') return '', 204 # success w/ no response! except (UnicodeDecodeError, DownloadError) as e: return _ANSI_ESCAPE_REXP.sub('', str(e)), 400 # send error message