Improve handling of livestreams, premieres with live_broadcast attr.
This commit is contained in:
parent
885bc2b711
commit
fa363a33c7
6 changed files with 92 additions and 14 deletions
|
@ -226,6 +226,7 @@ https://stackoverflow.com/a/35153397
|
|||
onclick="return action_button_passthrough(event, api.videos.mark_state, 'pending');"
|
||||
>Revert to Pending</button>
|
||||
|
||||
{% if video.live_broadcast is none %}
|
||||
<button
|
||||
{% if video.state == "pending" %}
|
||||
class="video_action_download"
|
||||
|
@ -234,6 +235,9 @@ https://stackoverflow.com/a/35153397
|
|||
{% endif %}
|
||||
onclick="return action_button_passthrough(event, api.videos.start_download);"
|
||||
>Download</button>
|
||||
{% else %}
|
||||
<button disabled>Video is {{video.live_broadcast}}</button>
|
||||
{% endif %}
|
||||
|
||||
<button
|
||||
{% if video.state == "pending" %}
|
||||
|
|
|
@ -249,6 +249,43 @@ def upgrade_7_to_8(ycdldb):
|
|||
# Redundant due to (state, published)
|
||||
ycdldb.sql.execute('DROP INDEX IF EXISTS index_video_state')
|
||||
|
||||
def upgrade_8_to_9(ycdldb):
|
||||
'''
|
||||
In this version, the `live_broadcast` column was added to the videos table.
|
||||
'''
|
||||
m = Migrator(ycdldb)
|
||||
|
||||
m.tables['videos']['create'] = '''
|
||||
CREATE TABLE IF NOT EXISTS videos(
|
||||
id TEXT,
|
||||
published INT,
|
||||
author_id TEXT,
|
||||
title TEXT,
|
||||
description TEXT,
|
||||
duration INT,
|
||||
views INT,
|
||||
thumbnail TEXT,
|
||||
live_broadcast TEXT,
|
||||
state TEXT
|
||||
);
|
||||
'''
|
||||
m.tables['videos']['transfer'] = '''
|
||||
INSERT INTO videos SELECT
|
||||
id,
|
||||
published,
|
||||
author_id,
|
||||
title,
|
||||
description,
|
||||
duration,
|
||||
views,
|
||||
thumbnail,
|
||||
NULL,
|
||||
state
|
||||
FROM videos_old;
|
||||
'''
|
||||
|
||||
m.go()
|
||||
|
||||
def upgrade_all(data_directory):
|
||||
'''
|
||||
Given the directory containing a ycdl database, apply all of the
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from voussoirkit import sqlhelpers
|
||||
|
||||
DATABASE_VERSION = 8
|
||||
DATABASE_VERSION = 9
|
||||
DB_VERSION_PRAGMA = f'''
|
||||
PRAGMA user_version = {DATABASE_VERSION};
|
||||
'''
|
||||
|
@ -32,6 +32,7 @@ CREATE TABLE IF NOT EXISTS videos(
|
|||
duration INT,
|
||||
views INT,
|
||||
thumbnail TEXT,
|
||||
live_broadcast TEXT,
|
||||
state TEXT
|
||||
);
|
||||
|
||||
|
|
|
@ -99,20 +99,33 @@ class Channel(Base):
|
|||
if not (force or status['new']):
|
||||
break
|
||||
|
||||
# Now we will refresh some other IDs that may not have been refreshed
|
||||
# by the previous loop.
|
||||
refresh_ids = set()
|
||||
|
||||
# 1. Videos which have become unlisted, therefore not returned by the
|
||||
# get_playlist_videos call. Take the set of all known ids minus those
|
||||
# refreshed by the earlier loop, the difference will be unlisted,
|
||||
# private, or deleted videos. At this time we have no special handling
|
||||
# for deleted videos, but they simply won't come back from ytapi.
|
||||
if force:
|
||||
# If some videos have become unlisted, then they will not have been
|
||||
# refreshed by the previous loop. So, take the set of all known ids
|
||||
# minus those refreshed by the loop, and try to refresh them.
|
||||
# Of course, it's possible they were deleted.
|
||||
known_ids = {v.id for v in self.ycdldb.get_videos(channel_id=self.id)}
|
||||
refresh_ids = list(known_ids.difference(seen_ids))
|
||||
refresh_ids.update(known_ids.difference(seen_ids))
|
||||
|
||||
# 2. Premieres or live events which may now be over but were not
|
||||
# included in the requested batch of IDs because they are not the most
|
||||
# recent.
|
||||
query = 'SELECT * FROM videos WHERE live_broadcast IS NOT NULL'
|
||||
videos = self.ycdldb.get_videos_by_sql(query)
|
||||
refresh_ids.update(v.id for v in videos)
|
||||
|
||||
if refresh_ids:
|
||||
self.ycdldb.log.debug(
|
||||
'%d ids did not come back from the generator, fetching them separately.',
|
||||
len(refresh_ids),
|
||||
)
|
||||
for video in self.ycdldb.youtube.get_videos(refresh_ids):
|
||||
self.ycdldb.insert_video(video, commit=False)
|
||||
self.ycdldb.log.debug('Refreshing %d ids separately.', len(refresh_ids))
|
||||
|
||||
# We call ingest_video instead of insert_video so that
|
||||
# premieres / livestreams which have finished can be automarked.
|
||||
for video_id in self.ycdldb.youtube.get_videos(refresh_ids):
|
||||
self.ycdldb.ingest_video(video_id, commit=False)
|
||||
|
||||
if commit:
|
||||
self.ycdldb.commit()
|
||||
|
@ -186,6 +199,7 @@ class Video(Base):
|
|||
self.duration = db_row['duration']
|
||||
self.views = db_row['views']
|
||||
self.thumbnail = db_row['thumbnail']
|
||||
self.live_broadcast = db_row['live_broadcast']
|
||||
self.state = db_row['state']
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -433,6 +433,13 @@ class YCDLDBVideoMixin:
|
|||
return status
|
||||
|
||||
if author.automark == 'downloaded':
|
||||
if video.live_broadcast is not None:
|
||||
self.log.debug(
|
||||
'Not downloading %s because live_broadcast=%s.',
|
||||
video.id,
|
||||
video.live_broadcast,
|
||||
)
|
||||
return status
|
||||
# download_video contains a call to mark_state.
|
||||
self.download_video(video.id, commit=False)
|
||||
else:
|
||||
|
@ -452,9 +459,11 @@ class YCDLDBVideoMixin:
|
|||
|
||||
try:
|
||||
existing = self.get_video(video.id)
|
||||
existing_live_broadcast = existing.live_broadcast
|
||||
download_status = existing.state
|
||||
except exceptions.NoSuchVideo:
|
||||
existing = None
|
||||
existing_live_broadcast = None
|
||||
download_status = 'pending'
|
||||
|
||||
data = {
|
||||
|
@ -466,6 +475,7 @@ class YCDLDBVideoMixin:
|
|||
'duration': video.duration,
|
||||
'views': video.views,
|
||||
'thumbnail': video.thumbnail['url'],
|
||||
'live_broadcast': video.live_broadcast,
|
||||
'state': download_status,
|
||||
}
|
||||
|
||||
|
@ -484,7 +494,16 @@ class YCDLDBVideoMixin:
|
|||
if commit:
|
||||
self.commit()
|
||||
|
||||
return {'new': not existing, 'video': video}
|
||||
# For the benefit of ingest_video, which will only apply the channel's
|
||||
# automark to newly released videos, let's consider the video to be
|
||||
# new if live_broadcast has changed to be None since last time.
|
||||
# This way, premieres and livestreams can be automarked by the next
|
||||
# refresh after they've ended.
|
||||
is_new = (
|
||||
(existing is None) or
|
||||
(existing_live_broadcast is not None and video.live_broadcast is None)
|
||||
)
|
||||
return {'new': is_new, 'video': video}
|
||||
|
||||
class YCDLDB(
|
||||
YCDLDBCacheManagerMixin,
|
||||
|
|
|
@ -32,6 +32,9 @@ class Video:
|
|||
# Something like '2016-10-01T21:00:01'
|
||||
self.published_string = snippet['publishedAt']
|
||||
self.published = isodate.parse_datetime(self.published_string).timestamp()
|
||||
self.live_broadcast = snippet['liveBroadcastContent']
|
||||
if self.live_broadcast == 'none':
|
||||
self.live_broadcast = None
|
||||
self.tags = snippet.get('tags', [])
|
||||
|
||||
self.duration = isodate.parse_duration(content_details['duration']).seconds
|
||||
|
|
Loading…
Reference in a new issue