1
0
Fork 0
mirror of synced 2024-05-19 20:12:30 +12:00

Change up downloading method

This commit is contained in:
Phxntxm 2017-07-02 22:20:01 -05:00
parent ffa1126f01
commit 042b2fcaea

View file

@ -1,9 +1,11 @@
import datetime
import traceback
import asyncio
from collections import deque
from itertools import islice
from random import shuffle
from .source import YoutubeDLSource
from .entry import URLPlaylistEntry, get_header
from .exceptions import ExtractionError, WrongEntryTypeError, LiveStreamError
from .event_emitter import EventEmitter
@ -38,82 +40,19 @@ class Playlist(EventEmitter):
return 0
async def add_entry(self, song_url, **meta):
"""
Validates and adds a song_url to be played. This does not start the download of the song.
"""Adds a song to this playlist"""
entry = YoutubeDLSource(self, song_url)
await entry.prepare()
self.entries.append(entry)
return entry
Returns the entry & the position it is in the queue.
:param song_url: The song url to add to the playlist.
:param meta: Any additional metadata to add to the playlist entry.
"""
try:
info = await self.downloader.extract_info(self.loop, song_url, download=False)
except Exception as e:
if "gaierror" in str(e) or "unknown url type" in str(e):
song_url = "ytsearch:" + song_url
try:
info = await self.downloader.extract_info(self.loop, song_url, download=False)
except Exception as e2:
raise ExtractionError('Could not extract information from {}\n\n{}'.format(song_url, e))
else:
raise ExtractionError('Could not extract information from {}\n\n{}'.format(song_url, e))
if not info:
raise ExtractionError('Could not extract information from %s' % song_url)
# TODO: Sort out what happens next when this happens
if info.get('_type', None) == 'playlist':
if info.get('extractor') == 'youtube:search':
if len(info['entries']) == 0:
raise ExtractionError('Could not extract information from %s' % song_url)
else:
info = info['entries'][0]
song_url = info['webpage_url']
else:
raise WrongEntryTypeError("This is a playlist.", True, info.get('webpage_url', None) or info.get('url', None))
if info['extractor'] in ['generic', 'Dropbox']:
try:
# unfortunately this is literally broken
# https://github.com/KeepSafe/aiohttp/issues/758
# https://github.com/KeepSafe/aiohttp/issues/852
headers = await get_header(info['url'])
content_type = headers.get('Content-Type')
except Exception as e:
content_type = None
if content_type:
if content_type.startswith(('application/', 'image/')):
if '/ogg' not in content_type: # How does a server say `application/ogg` what the actual fuck
raise ExtractionError("Invalid content type \"%s\" for url %s" % (content_type, song_url))
if headers.get('ice-audio-info'):
raise LiveStreamError("Cannot download from a livestream")
if info.get('is_live', False):
raise LiveStreamError("Cannot download from a livestream")
entry = URLPlaylistEntry(
self,
song_url,
info.get('title', 'Untitled'),
info.get('thumbnail', None),
info.get('duration', 0) or 0,
self.downloader.ytdl.prepare_filename(info),
**meta
)
self._add_entry(entry)
return entry, len(self.entries)
async def import_from(self, playlist_url, **meta):
async def import_from(self, playlist_url, requester):
"""
Imports the songs from `playlist_url` and queues them to be played.
Returns a list of `entries` that have been enqueued.
:param playlist_url: The playlist url to be cut into individual urls and added to the playlist
:param meta: Any additional metadata to add to the playlist entry
"""
position = len(self.entries) + 1
entry_list = []
@ -132,29 +71,21 @@ class Playlist(EventEmitter):
else:
url_field = 'webpage_url'
baditems = 0
yield len(info['entries'])
for items in info['entries']:
if items:
entry = YoutubeDLSource(self, items[url_field])
try:
entry = URLPlaylistEntry(
self,
items[url_field],
items.get('title', 'Untitled'),
items.get('duration', 0) or 0,
self.downloader.ytdl.prepare_filename(items),
**meta
)
self._add_entry(entry)
entry_list.append(entry)
await entry.prepare()
except:
baditems += 1
# Once I know more about what's happening here I can add a proper message
traceback.print_exc()
yield False
else:
entry.requester = requester
self.entries.append(entry)
yield True
else:
baditems += 1
return entry_list, position
yield False
async def async_process_youtube_playlist(self, playlist_url, **meta):
"""
@ -227,30 +158,22 @@ class Playlist(EventEmitter):
def _add_entry(self, entry):
self.entries.append(entry)
self.emit('entry-added', playlist=self, entry=entry)
if self.peek() is entry:
entry.get_ready_future()
async def next_entry(self):
"""Get the next song in the playlist; this class will wait until the next song is ready"""
entry = self.peek()
async def get_next_entry(self, predownload_next=True):
"""
A coroutine which will return the next song or None if no songs left to play.
# While we have an entry available
while entry:
# Check if we are ready or if we've errored, either way we'll pop it from the deque
if entry.ready or entry.error:
return self.entries.popleft()
# Otherwise, wait a second and check again
else:
await asyncio.sleep(1)
Additionally, if predownload_next is set to True, it will attempt to download the next
song to be played - so that it's ready by the time we get to it.
"""
if not self.entries:
return None
entry = self.entries.popleft()
if predownload_next:
next_entry = self.peek()
if next_entry:
next_entry.get_ready_future()
fut = entry.get_ready_future()
return fut, await fut
# If we've reached here, we have no entries
return None
def peek(self):
"""
@ -259,17 +182,5 @@ class Playlist(EventEmitter):
if self.entries:
return self.entries[0]
async def estimate_time_until(self, position, player):
"""
(very) Roughly estimates the time till the queue will 'position'
"""
estimated_time = sum([e.duration for e in islice(self.entries, position - 1)])
# When the player plays a song, it eats the first playlist item, so we just have to add the time back
if not player.is_stopped and player.current_entry:
estimated_time += player.current_entry.duration - player.progress
return datetime.timedelta(seconds=estimated_time)
def count_for_user(self, user):
return sum(1 for e in self.entries if e.meta.get('author', None) == user)