Channels: fix statistics inflated by tagged videos

The tag_file join in Channel.get_statistics() produced one row per tag,
so a Video with N Tags was counted N times in video_count, size, and
length. Count tagged Videos in a subquery instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Roland 2026-07-03 17:29:24 -06:00
parent 8044fd1240
commit 64b1ca8029
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
2 changed files with 43 additions and 7 deletions

View file

@ -110,6 +110,40 @@ async def test_channel_download_relationships(test_session, download_channel):
assert test_session.query(Download).count() == 0
@pytest.mark.asyncio
async def test_channel_statistics_with_tagged_videos(async_client, test_session, channel_factory, video_factory,
tag_factory):
"""Tagging a Video must not inflate its Channel's statistics.
A Video with multiple Tags was counted once per Tag because of the tag_file join."""
channel = channel_factory()
video1 = video_factory(channel_id=channel.id)
video2 = video_factory(channel_id=channel.id)
video3 = video_factory(channel_id=channel.id)
test_session.commit()
expected_size = video1.file_group.size + video2.file_group.size + video3.file_group.size
# Statistics are correct before any Videos are tagged.
statistics = channel.get_statistics()
assert statistics['video_count'] == 3
assert statistics['size'] == expected_size
assert statistics['video_tags'] == 0
# video1 has two Tags, video2 has one Tag, video3 has none.
tag1, tag2 = await tag_factory(), await tag_factory()
video1.add_tag(test_session, tag1.name)
video1.add_tag(test_session, tag2.name)
video2.add_tag(test_session, tag1.name)
test_session.commit()
# Tagging Videos must not change the number of Videos in the Channel.
statistics = channel.get_statistics()
assert statistics['video_count'] == 3
assert statistics['size'] == expected_size
assert statistics['video_tags'] == 2
def test_channel_info_json(test_session, test_directory):
from wrolpi.collections import Collection

View file

@ -953,15 +953,17 @@ class Channel(ModelHelper, Base):
"""Get statistics about this channel."""
with get_db_curs() as curs:
stmt = '''
SELECT SUM(size) AS "size",
MAX(size) AS "largest_video",
COUNT(video.id) AS "video_count",
SUM(fg.length) AS "length",
-- Videos may use multiple tags.
COUNT(video.id) FILTER ( WHERE tf.file_group_id IS NOT NULL ) AS "video_tags"
SELECT SUM(size) AS "size",
MAX(size) AS "largest_video",
COUNT(video.id) AS "video_count",
SUM(fg.length) AS "length",
-- Count Videos with at least one Tag; a Video may use multiple Tags.
(SELECT COUNT(DISTINCT tf.file_group_id)
FROM tag_file tf
INNER JOIN video v ON v.file_group_id = tf.file_group_id
WHERE v.channel_id = %(id)s) AS "video_tags"
FROM video
LEFT JOIN file_group fg on fg.id = video.file_group_id
LEFT JOIN public.tag_file tf on fg.id = tf.file_group_id
WHERE channel_id = %(id)s \
'''
curs.execute(stmt, dict(id=self.id))