mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
283 lines
12 KiB
Python
283 lines
12 KiB
Python
import json
|
|
from datetime import datetime, timezone
|
|
from http import HTTPStatus
|
|
|
|
import pytest
|
|
|
|
from modules.videos.models import Video
|
|
from wrolpi.errors import FileGroupIsTagged
|
|
from wrolpi.files.models import FileGroup
|
|
|
|
|
|
def test_delete_video_no_channel(test_session, simple_video):
|
|
"""A Video can be deleted even if it does not have a channel."""
|
|
assert simple_video.channel
|
|
simple_video.channel_id = None
|
|
test_session.commit()
|
|
|
|
paths = list(simple_video.file_group.my_paths())
|
|
|
|
assert not simple_video.channel
|
|
simple_video.delete()
|
|
|
|
# All files were deleted.
|
|
for path in paths:
|
|
assert not path.exists()
|
|
|
|
test_session.commit()
|
|
|
|
assert test_session.query(FileGroup).count() == 0
|
|
assert test_session.query(Video).count() == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_video_with_tag(async_client, test_session, video_factory, tag_factory):
|
|
"""You cannot delete a video with a tag."""
|
|
video = video_factory(with_video_file=True, with_poster_ext='png')
|
|
tag = await tag_factory()
|
|
video.add_tag(test_session, tag.name)
|
|
test_session.commit()
|
|
|
|
with pytest.raises(FileGroupIsTagged):
|
|
video.delete()
|
|
|
|
# Video was not deleted.
|
|
test_session.commit()
|
|
assert test_session.query(Video).count() == 1
|
|
assert test_session.query(FileGroup).count() == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_video_api_tagged_requires_force(async_client, test_session, video_factory, tag_factory,
|
|
await_switches):
|
|
"""The unified delete endpoint returns 409 for tagged videos until force=true is passed."""
|
|
video = video_factory(with_video_file=True, with_poster_ext='png')
|
|
tag = await tag_factory()
|
|
video.add_tag(test_session, tag.name)
|
|
test_session.commit()
|
|
file_group_id = video.file_group_id
|
|
paths = list(video.file_group.my_paths())
|
|
|
|
body = json.dumps({'file_group_ids': [file_group_id]})
|
|
request, response = await async_client.post('/api/files/delete_groups', content=body)
|
|
assert response.status_code == HTTPStatus.CONFLICT
|
|
assert response.json['code'] == 'FILE_GROUP_IS_TAGGED'
|
|
assert len(response.json['file_groups']) == 1
|
|
assert response.json['file_groups'][0]['id'] == file_group_id
|
|
assert tag.name in response.json['file_groups'][0]['tags']
|
|
# Nothing was deleted.
|
|
assert all(p.is_file() for p in paths)
|
|
assert test_session.query(Video).count() == 1
|
|
|
|
# With force=true, the Video is deleted.
|
|
body = json.dumps({'file_group_ids': [file_group_id], 'force': True})
|
|
request, response = await async_client.post('/api/files/delete_groups', content=body)
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
assert all(not p.exists() for p in paths)
|
|
assert test_session.query(Video).count() == 0
|
|
assert test_session.query(FileGroup).count() == 0
|
|
await await_switches()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_video_api_atomic_when_some_tagged(async_client, test_session, video_factory, tag_factory):
|
|
"""When deleting multiple Videos and any are tagged, NONE are deleted until force=true."""
|
|
untagged = video_factory(with_video_file=True)
|
|
tagged = video_factory(with_video_file=True)
|
|
tag = await tag_factory()
|
|
tagged.add_tag(test_session, tag.name)
|
|
test_session.commit()
|
|
untagged_paths = list(untagged.file_group.my_paths())
|
|
tagged_paths = list(tagged.file_group.my_paths())
|
|
|
|
body = json.dumps({'file_group_ids': [untagged.file_group_id, tagged.file_group_id]})
|
|
request, response = await async_client.post('/api/files/delete_groups', content=body)
|
|
assert response.status_code == HTTPStatus.CONFLICT
|
|
assert response.json['code'] == 'FILE_GROUP_IS_TAGGED'
|
|
returned_ids = {fg['id'] for fg in response.json['file_groups']}
|
|
assert returned_ids == {tagged.file_group_id}
|
|
# Nothing was deleted.
|
|
assert all(p.is_file() for p in untagged_paths)
|
|
assert all(p.is_file() for p in tagged_paths)
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_video_channel_refresh(async_client, test_session, test_directory, channel_factory, video_factory,
|
|
refresh_files):
|
|
"""A Video will be associated with a Channel when it's files are in that Channel's directory."""
|
|
# Create a video file in this channel's directory.
|
|
channel = channel_factory()
|
|
video1_path = channel.directory / 'video1.mp4'
|
|
video_factory(channel_id=channel.id, with_video_file=video1_path, with_poster_ext='jpg')
|
|
# Create another video outside the channel's directory. It should not have a channel.
|
|
video2_path = test_directory / 'video2.mp4'
|
|
video_factory(with_video_file=video2_path)
|
|
# Delete all FileGroups so the channel will be associated again.
|
|
test_session.query(FileGroup).delete()
|
|
test_session.commit()
|
|
|
|
await refresh_files()
|
|
|
|
assert test_session.query(FileGroup).count() == 2
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
# The video is associated with `simple_channel` because its file is in the Channel's directory.
|
|
video1: Video = Video.get_by_path(test_session, video1_path)
|
|
video2: Video = Video.get_by_path(test_session, video2_path)
|
|
assert video1.channel == channel
|
|
assert not video2.channel
|
|
assert video1.__json__()['video']['channel']
|
|
video_channel = video1.__json__()['video']['channel']
|
|
assert video_channel['id'] == channel.id
|
|
assert video_channel['name'] == channel.name
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_duplicate_video(async_client, test_session, channel_factory, video_factory, tag_factory):
|
|
"""If duplicate Video's exist, and everything about the files matches, delete a random one."""
|
|
channel = channel_factory(name='Channel Name')
|
|
video_path = channel.directory / f'{channel.name}_20000101_ABC123456_The video title.mp4'
|
|
|
|
channel.info_json = {'entries': [{'id': 'ABC123456', 'title': 'The video title'}]}
|
|
entry = channel.info_json['entries'][0]
|
|
|
|
tag = await tag_factory()
|
|
vid1 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title',
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
tag_names=[tag.name, ]
|
|
)
|
|
vid2 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title ', # Video was renamed, trailing space was removed.
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
)
|
|
vid1.file_group.url = vid2.file_group.url = 'https://example.com/video'
|
|
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
test_session.commit()
|
|
|
|
assert await Video.delete_duplicate_videos(test_session, 'https://example.com/video', entry['id'], video_path)
|
|
test_session.commit()
|
|
|
|
assert test_session.query(Video).count() == 1, 'Duplicate video was not deleted.'
|
|
video = test_session.query(Video).one()
|
|
assert video.video_path == video_path, 'Video path does not match'
|
|
assert video.video_path.is_file()
|
|
assert set(channel.directory.iterdir()) == set(video.file_group.my_paths())
|
|
assert video.file_group.tag_names == [tag.name, ]
|
|
|
|
# Running again has no effect.
|
|
assert not await Video.delete_duplicate_videos(test_session, 'https://example.com/video', entry['id'], video_path)
|
|
test_session.commit()
|
|
|
|
assert test_session.query(Video).count() == 1, 'Duplicate video was not deleted.'
|
|
video = test_session.query(Video).one()
|
|
assert video.video_path.is_file()
|
|
assert video.video_path == video_path, 'Video path does not match'
|
|
assert set(channel.directory.iterdir()) == set(video.file_group.my_paths())
|
|
assert video.file_group.tag_names == [tag.name, ]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_duplicate_video_tagged_duplicate(async_client, test_session, channel_factory, video_factory,
|
|
tag_factory):
|
|
"""Deleting a duplicate Video should work even when the duplicate being deleted has tags."""
|
|
channel = channel_factory(name='Channel Name')
|
|
video_path = channel.directory / f'{channel.name}_20000101_ABC123456_The video title.mp4'
|
|
|
|
channel.info_json = {'entries': [{'id': 'ABC123456', 'title': 'The video title'}]}
|
|
entry = channel.info_json['entries'][0]
|
|
|
|
tag = await tag_factory()
|
|
vid1 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title',
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
)
|
|
vid2 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title ',
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
tag_names=[tag.name, ], # The duplicate being deleted has a tag.
|
|
)
|
|
vid1.file_group.url = vid2.file_group.url = 'https://example.com/video'
|
|
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
test_session.commit()
|
|
|
|
assert await Video.delete_duplicate_videos(test_session, 'https://example.com/video', entry['id'], video_path)
|
|
test_session.commit()
|
|
|
|
assert test_session.query(Video).count() == 1, 'Duplicate video was not deleted.'
|
|
video = test_session.query(Video).one()
|
|
assert video.video_path == video_path
|
|
assert video.video_path.is_file()
|
|
# The tag from the deleted duplicate should be consolidated onto the kept video.
|
|
assert video.file_group.tag_names == [tag.name, ]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_renamed_video(async_client, test_session, channel_factory, video_factory, tag_factory):
|
|
"""If duplicate Video's exist, delete all the videos that do not have the new title."""
|
|
channel = channel_factory(name='Channel Name')
|
|
video_path = channel.directory / f'{channel.name}_20000101_ABC123456_Some new title.mp4'
|
|
|
|
channel.info_json = {'entries': [{'id': 'ABC123456', 'title': 'Some new title'}]}
|
|
entry = channel.info_json['entries'][0]
|
|
|
|
tag = await tag_factory()
|
|
vid1 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title',
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
tag_names=[tag.name, ]
|
|
)
|
|
vid2 = video_factory(
|
|
channel_id=channel.id,
|
|
title=f'{channel.name}_20000101_ABC123456_The video title ', # Video was renamed, trailing space was removed.
|
|
upload_date=datetime(2000, 1, 1, tzinfo=timezone.utc),
|
|
source_id='ABC123456',
|
|
with_video_file=True,
|
|
with_info_json=True,
|
|
with_caption_file=True,
|
|
)
|
|
vid1.file_group.url = vid2.file_group.url = 'https://example.com/video'
|
|
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
test_session.commit()
|
|
|
|
await Video.delete_duplicate_videos(test_session, 'https://example.com/video', entry['id'], video_path)
|
|
test_session.commit()
|
|
|
|
assert test_session.query(Video).count() == 1, 'Duplicate video was not deleted.'
|
|
video = test_session.query(Video).one()
|
|
assert video.video_path == video_path, 'Video path does not match'
|
|
assert video.video_path.is_file()
|
|
assert set(channel.directory.iterdir()) == set(video.file_group.my_paths())
|
|
assert video.file_group.tag_names == [tag.name, ]
|