ffn
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,5 +20,6 @@ venv/
|
|||||||
|
|
||||||
*.mkv
|
*.mkv
|
||||||
*.webm
|
*.webm
|
||||||
|
*.mp4
|
||||||
ffmpeg2pass-0.log
|
ffmpeg2pass-0.log
|
||||||
*.sup
|
*.sup
|
||||||
@@ -8,6 +8,7 @@ from textual.widgets import Button, Footer, Header, Input, Static
|
|||||||
from textual.widgets._data_table import CellDoesNotExist
|
from textual.widgets._data_table import CellDoesNotExist
|
||||||
|
|
||||||
from ffx.file_properties import FileProperties
|
from ffx.file_properties import FileProperties
|
||||||
|
from ffx.helper import DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY
|
||||||
from ffx.media_descriptor_change_set import MediaDescriptorChangeSet
|
from ffx.media_descriptor_change_set import MediaDescriptorChangeSet
|
||||||
from ffx.show_descriptor import ShowDescriptor
|
from ffx.show_descriptor import ShowDescriptor
|
||||||
from ffx.track_descriptor import TrackDescriptor
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
@@ -207,6 +208,30 @@ class InspectDetailsScreen(MediaWorkflowScreenBase):
|
|||||||
def action_back(self):
|
def action_back(self):
|
||||||
go_back_or_exit(self)
|
go_back_or_exit(self)
|
||||||
|
|
||||||
|
def getDisplayedMediaDescriptor(self):
|
||||||
|
if self._currentPattern is not None and self._targetMediaDescriptor is not None:
|
||||||
|
return self._targetMediaDescriptor
|
||||||
|
return self._sourceMediaDescriptor
|
||||||
|
|
||||||
|
def getTrackEditSourceDescriptor(self):
|
||||||
|
selectedTrackDescriptor = self.getSelectedTrackDescriptor()
|
||||||
|
if (
|
||||||
|
selectedTrackDescriptor is None
|
||||||
|
or self._currentPattern is None
|
||||||
|
or self._targetMediaDescriptor is None
|
||||||
|
):
|
||||||
|
return selectedTrackDescriptor
|
||||||
|
|
||||||
|
for sourceTrackDescriptor in self._sourceMediaDescriptor.getTrackDescriptors():
|
||||||
|
if (
|
||||||
|
sourceTrackDescriptor.getSourceIndex()
|
||||||
|
== selectedTrackDescriptor.getSourceIndex()
|
||||||
|
and sourceTrackDescriptor.getType() == selectedTrackDescriptor.getType()
|
||||||
|
):
|
||||||
|
return sourceTrackDescriptor
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _build_shows_table(self):
|
def _build_shows_table(self):
|
||||||
from textual.widgets import DataTable
|
from textual.widgets import DataTable
|
||||||
|
|
||||||
@@ -478,8 +503,6 @@ class InspectDetailsScreen(MediaWorkflowScreenBase):
|
|||||||
self.updateDifferences()
|
self.updateDifferences()
|
||||||
return updated
|
return updated
|
||||||
|
|
||||||
self.reloadProperties(reset_draft=True)
|
|
||||||
|
|
||||||
tagDifferences = self._mediaChangeSetObj.get(MediaDescriptorChangeSet.TAGS_KEY, {})
|
tagDifferences = self._mediaChangeSetObj.get(MediaDescriptorChangeSet.TAGS_KEY, {})
|
||||||
for addedTagKey in tagDifferences.get(DIFF_ADDED_KEY, {}).keys():
|
for addedTagKey in tagDifferences.get(DIFF_ADDED_KEY, {}).keys():
|
||||||
self._tac.deleteMediaTagByKey(self._currentPattern.getId(), addedTagKey)
|
self._tac.deleteMediaTagByKey(self._currentPattern.getId(), addedTagKey)
|
||||||
@@ -566,9 +589,6 @@ class InspectDetailsScreen(MediaWorkflowScreenBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def handle_edit_pattern(self, screenResult):
|
def handle_edit_pattern(self, screenResult):
|
||||||
if not screenResult:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.reloadProperties(reset_draft=True)
|
self.reloadProperties(reset_draft=True)
|
||||||
if self._currentPattern is not None:
|
if self._currentPattern is not None:
|
||||||
self.query_one("#pattern_input", Input).value = self._currentPattern.getPattern()
|
self.query_one("#pattern_input", Input).value = self._currentPattern.getPattern()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
from textual import work
|
from textual import events, work
|
||||||
from textual.containers import Grid
|
from textual.containers import Grid
|
||||||
from textual.worker import Worker, WorkerState
|
from textual.worker import Worker, WorkerState
|
||||||
from textual.widgets import Button, Footer, Header, Static
|
from textual.widgets import Button, Footer, Header, Static
|
||||||
@@ -183,6 +183,13 @@ class MediaEditScreen(MediaWorkflowScreenBase):
|
|||||||
self.updateToggleButtons()
|
self.updateToggleButtons()
|
||||||
self._applyChangesWorker = None
|
self._applyChangesWorker = None
|
||||||
|
|
||||||
|
def on_screen_resume(self, _event: events.ScreenResume) -> None:
|
||||||
|
if not hasattr(self, "tracksTable"):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.refreshAfterDraftChange()
|
||||||
|
self.updateToggleButtons()
|
||||||
|
|
||||||
def _update_grid_layout(self) -> None:
|
def _update_grid_layout(self) -> None:
|
||||||
leftColumnWidth = max(
|
leftColumnWidth = max(
|
||||||
localized_column_width(t("File"), self.GRID_COLUMN_LABEL_MIN),
|
localized_column_width(t("File"), self.GRID_COLUMN_LABEL_MIN),
|
||||||
@@ -353,30 +360,35 @@ class MediaEditScreen(MediaWorkflowScreenBase):
|
|||||||
if trackDescriptor is None:
|
if trackDescriptor is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
updatedTracks = []
|
nextSourceMediaDescriptor = self._sourceMediaDescriptor.clone(context=self.context)
|
||||||
|
updatedTracks = nextSourceMediaDescriptor.getTrackDescriptors()
|
||||||
|
replacementTrack = trackDescriptor.clone(context=self.context)
|
||||||
replaced = False
|
replaced = False
|
||||||
for currentTrack in self._sourceMediaDescriptor.getTrackDescriptors():
|
|
||||||
if (
|
for trackIndex, currentTrack in enumerate(updatedTracks):
|
||||||
currentTrack.getIndex() == trackDescriptor.getIndex()
|
sameSourceTrack = (
|
||||||
and currentTrack.getSubIndex() == trackDescriptor.getSubIndex()
|
currentTrack.getSourceIndex() == replacementTrack.getSourceIndex()
|
||||||
):
|
and currentTrack.getType() == replacementTrack.getType()
|
||||||
updatedTracks.append(trackDescriptor)
|
)
|
||||||
|
sameVisibleTrack = (
|
||||||
|
currentTrack.getIndex() == replacementTrack.getIndex()
|
||||||
|
and currentTrack.getSubIndex() == replacementTrack.getSubIndex()
|
||||||
|
)
|
||||||
|
if sameSourceTrack or sameVisibleTrack:
|
||||||
|
updatedTracks[trackIndex] = replacementTrack
|
||||||
replaced = True
|
replaced = True
|
||||||
else:
|
break
|
||||||
updatedTracks.append(currentTrack)
|
|
||||||
|
|
||||||
if not replaced:
|
if not replaced:
|
||||||
self.setMessage(t("Unable to update selected stream."))
|
self.setMessage(t("Unable to update selected stream."))
|
||||||
return
|
return
|
||||||
|
|
||||||
self._sourceMediaDescriptor = self._sourceMediaDescriptor.clone(context=self.context)
|
self._sourceMediaDescriptor = nextSourceMediaDescriptor
|
||||||
self._sourceMediaDescriptor.getTrackDescriptors().clear()
|
|
||||||
self._sourceMediaDescriptor.getTrackDescriptors().extend(updatedTracks)
|
|
||||||
self.setMessage(
|
self.setMessage(
|
||||||
t(
|
t(
|
||||||
"Updated stream #{index} ({track_type}).",
|
"Updated stream #{index} ({track_type}).",
|
||||||
index=trackDescriptor.getIndex(),
|
index=replacementTrack.getIndex(),
|
||||||
track_type=t(trackDescriptor.getType().label()),
|
track_type=t(replacementTrack.getType().label()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.refreshAfterDraftChange()
|
self.refreshAfterDraftChange()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from ffx.audio_layout import AudioLayout
|
|||||||
from ffx.file_properties import FileProperties
|
from ffx.file_properties import FileProperties
|
||||||
from ffx.helper import DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY
|
from ffx.helper import DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY
|
||||||
from ffx.iso_language import IsoLanguage
|
from ffx.iso_language import IsoLanguage
|
||||||
|
from ffx.media_descriptor import MediaDescriptor
|
||||||
from ffx.media_descriptor_change_set import MediaDescriptorChangeSet
|
from ffx.media_descriptor_change_set import MediaDescriptorChangeSet
|
||||||
from ffx.track_descriptor import TrackDescriptor
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
from ffx.track_disposition import TrackDisposition
|
from ffx.track_disposition import TrackDisposition
|
||||||
@@ -171,10 +172,17 @@ class MediaWorkflowScreenBase(Screen):
|
|||||||
def hasPendingChanges(self) -> bool:
|
def hasPendingChanges(self) -> bool:
|
||||||
return bool(self._mediaChangeSetObj)
|
return bool(self._mediaChangeSetObj)
|
||||||
|
|
||||||
|
def getDisplayedMediaDescriptor(self) -> MediaDescriptor | None:
|
||||||
|
return self._sourceMediaDescriptor
|
||||||
|
|
||||||
|
def getTrackEditSourceDescriptor(self) -> TrackDescriptor | None:
|
||||||
|
return self.getSelectedTrackDescriptor()
|
||||||
|
|
||||||
def updateMediaTags(self):
|
def updateMediaTags(self):
|
||||||
|
displayedMediaDescriptor = self.getDisplayedMediaDescriptor()
|
||||||
self._sourceMediaTagRowData = populate_tag_table(
|
self._sourceMediaTagRowData = populate_tag_table(
|
||||||
self.mediaTagsTable,
|
self.mediaTagsTable,
|
||||||
self._sourceMediaDescriptor.getTags(),
|
displayedMediaDescriptor.getTags() if displayedMediaDescriptor is not None else {},
|
||||||
ignore_keys=self._ignoreGlobalKeys,
|
ignore_keys=self._ignoreGlobalKeys,
|
||||||
remove_keys=self._removeGlobalKeys,
|
remove_keys=self._removeGlobalKeys,
|
||||||
)
|
)
|
||||||
@@ -184,7 +192,12 @@ class MediaWorkflowScreenBase(Screen):
|
|||||||
self._configure_tracks_table_columns()
|
self._configure_tracks_table_columns()
|
||||||
self._trackRowData = {}
|
self._trackRowData = {}
|
||||||
|
|
||||||
trackDescriptorList = self._sourceMediaDescriptor.getTrackDescriptors()
|
displayedMediaDescriptor = self.getDisplayedMediaDescriptor()
|
||||||
|
trackDescriptorList = (
|
||||||
|
displayedMediaDescriptor.getTrackDescriptors()
|
||||||
|
if displayedMediaDescriptor is not None
|
||||||
|
else []
|
||||||
|
)
|
||||||
typeCounter = {}
|
typeCounter = {}
|
||||||
applyNormalization = bool(getattr(self, "_applyNormalization", False))
|
applyNormalization = bool(getattr(self, "_applyNormalization", False))
|
||||||
|
|
||||||
@@ -366,7 +379,7 @@ class MediaWorkflowScreenBase(Screen):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def setSelectedTrackDefault(self):
|
def setSelectedTrackDefault(self):
|
||||||
selectedTrackDescriptor = self.getSelectedTrackDescriptor()
|
selectedTrackDescriptor = self.getTrackEditSourceDescriptor()
|
||||||
if selectedTrackDescriptor is None:
|
if selectedTrackDescriptor is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -377,7 +390,7 @@ class MediaWorkflowScreenBase(Screen):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def setSelectedTrackForced(self):
|
def setSelectedTrackForced(self):
|
||||||
selectedTrackDescriptor = self.getSelectedTrackDescriptor()
|
selectedTrackDescriptor = self.getTrackEditSourceDescriptor()
|
||||||
if selectedTrackDescriptor is None:
|
if selectedTrackDescriptor is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,11 @@ def apply_metadata_edits(
|
|||||||
*,
|
*,
|
||||||
notify=None,
|
notify=None,
|
||||||
) -> dict[str, object]:
|
) -> dict[str, object]:
|
||||||
|
|
||||||
temporaryOutputPath = create_temporary_output_path(source_path)
|
temporaryOutputPath = create_temporary_output_path(source_path)
|
||||||
|
|
||||||
editContext = build_metadata_edit_context(context)
|
editContext = build_metadata_edit_context(context)
|
||||||
|
|
||||||
commandSequence = build_metadata_edit_command(
|
commandSequence = build_metadata_edit_command(
|
||||||
editContext,
|
editContext,
|
||||||
source_path,
|
source_path,
|
||||||
@@ -112,17 +115,21 @@ def apply_metadata_edits(
|
|||||||
baseline_descriptor,
|
baseline_descriptor,
|
||||||
draft_descriptor,
|
draft_descriptor,
|
||||||
)
|
)
|
||||||
|
|
||||||
ffmpegSeconds = 0.0
|
ffmpegSeconds = 0.0
|
||||||
replaceSeconds = 0.0
|
replaceSeconds = 0.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
if editContext.get("dry_run", False):
|
if editContext.get("dry_run", False):
|
||||||
|
|
||||||
notify_ffmpeg_invocation(
|
notify_ffmpeg_invocation(
|
||||||
editContext,
|
editContext,
|
||||||
commandSequence,
|
commandSequence,
|
||||||
notify=notify,
|
notify=notify,
|
||||||
dry_run=True,
|
dry_run=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"applied": False,
|
"applied": False,
|
||||||
"dry_run": True,
|
"dry_run": True,
|
||||||
@@ -136,15 +143,18 @@ def apply_metadata_edits(
|
|||||||
}
|
}
|
||||||
|
|
||||||
notify_ffmpeg_invocation(editContext, commandSequence, notify=notify)
|
notify_ffmpeg_invocation(editContext, commandSequence, notify=notify)
|
||||||
|
|
||||||
ffmpegStart = monotonic()
|
ffmpegStart = monotonic()
|
||||||
_out, err, rc = executeProcess(commandSequence, context=editContext)
|
_out, err, rc = executeProcess(commandSequence, context=editContext)
|
||||||
ffmpegSeconds = monotonic() - ffmpegStart
|
ffmpegSeconds = monotonic() - ffmpegStart
|
||||||
|
|
||||||
if rc:
|
if rc:
|
||||||
raise click.ClickException(f"ffmpeg edit failed: rc={rc} error={err}")
|
raise click.ClickException(f"ffmpeg edit failed: rc={rc} error={err}")
|
||||||
|
|
||||||
replaceStart = monotonic()
|
replaceStart = monotonic()
|
||||||
os.replace(temporaryOutputPath, source_path)
|
os.replace(temporaryOutputPath, source_path)
|
||||||
replaceSeconds = monotonic() - replaceStart
|
replaceSeconds = monotonic() - replaceStart
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"applied": True,
|
"applied": True,
|
||||||
"dry_run": False,
|
"dry_run": False,
|
||||||
@@ -156,6 +166,7 @@ def apply_metadata_edits(
|
|||||||
"write_seconds": ffmpegSeconds + replaceSeconds,
|
"write_seconds": ffmpegSeconds + replaceSeconds,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
if os.path.exists(temporaryOutputPath):
|
if os.path.exists(temporaryOutputPath):
|
||||||
os.remove(temporaryOutputPath)
|
os.remove(temporaryOutputPath)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import click, re
|
import click, re
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from textual import events
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.widgets import Header, Footer, Static, Button, Input, DataTable, TextArea
|
from textual.widgets import Header, Footer, Static, Button, Input, DataTable, TextArea
|
||||||
from textual.containers import Grid
|
from textual.containers import Grid
|
||||||
@@ -342,6 +343,16 @@ class PatternDetailsScreen(Screen):
|
|||||||
self.updateTracks()
|
self.updateTracks()
|
||||||
self.updateShiftedSeasons()
|
self.updateShiftedSeasons()
|
||||||
|
|
||||||
|
def on_screen_resume(self, _event: events.ScreenResume) -> None:
|
||||||
|
if not hasattr(self, "tracksTable") or not hasattr(self, "tagsTable"):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.updateTags()
|
||||||
|
self.updateTracks()
|
||||||
|
|
||||||
|
if self.__pattern is not None and hasattr(self, "shiftedSeasonsTable"):
|
||||||
|
self.updateShiftedSeasons()
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
|
|
||||||
|
|
||||||
@@ -430,9 +441,9 @@ class PatternDetailsScreen(Screen):
|
|||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
|
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
|
|
||||||
# Row 10
|
# Row 10
|
||||||
yield self.shiftedSeasonsTable
|
yield self.shiftedSeasonsTable
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ if str(SRC_ROOT) not in sys.path:
|
|||||||
|
|
||||||
|
|
||||||
from ffx.audio_layout import AudioLayout # noqa: E402
|
from ffx.audio_layout import AudioLayout # noqa: E402
|
||||||
|
from ffx.helper import DIFF_ADDED_KEY # noqa: E402
|
||||||
from ffx.iso_language import IsoLanguage # noqa: E402
|
from ffx.iso_language import IsoLanguage # noqa: E402
|
||||||
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
||||||
from ffx.inspect_details_screen import InspectDetailsScreen # noqa: E402
|
from ffx.inspect_details_screen import InspectDetailsScreen # noqa: E402
|
||||||
from ffx.i18n import set_current_language # noqa: E402
|
from ffx.i18n import set_current_language # noqa: E402
|
||||||
|
from ffx.media_descriptor import MediaDescriptor # noqa: E402
|
||||||
from ffx.media_edit_screen import MediaEditScreen # noqa: E402
|
from ffx.media_edit_screen import MediaEditScreen # noqa: E402
|
||||||
from ffx.pattern_details_screen import PatternDetailsScreen # noqa: E402
|
from ffx.pattern_details_screen import PatternDetailsScreen # noqa: E402
|
||||||
from ffx.show_descriptor import ShowDescriptor # noqa: E402
|
from ffx.show_descriptor import ShowDescriptor # noqa: E402
|
||||||
@@ -89,12 +91,16 @@ class FakeTagTable:
|
|||||||
|
|
||||||
|
|
||||||
class FakeMediaDescriptor:
|
class FakeMediaDescriptor:
|
||||||
def __init__(self, track_descriptors):
|
def __init__(self, track_descriptors, tags=None):
|
||||||
self._track_descriptors = list(track_descriptors)
|
self._track_descriptors = list(track_descriptors)
|
||||||
|
self._tags = dict(tags or {})
|
||||||
|
|
||||||
def getTrackDescriptors(self):
|
def getTrackDescriptors(self):
|
||||||
return list(self._track_descriptors)
|
return list(self._track_descriptors)
|
||||||
|
|
||||||
|
def getTags(self):
|
||||||
|
return dict(self._tags)
|
||||||
|
|
||||||
|
|
||||||
class FakeValueWidget:
|
class FakeValueWidget:
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
@@ -459,6 +465,99 @@ class TagTableScreenStateTests(unittest.TestCase):
|
|||||||
calls,
|
calls,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_media_edit_screen_handle_edit_track_updates_draft_descriptor(self):
|
||||||
|
original_track = TrackDescriptor(
|
||||||
|
index=1,
|
||||||
|
source_index=1,
|
||||||
|
sub_index=0,
|
||||||
|
track_type=TrackType.SUBTITLE,
|
||||||
|
codec_name=TrackCodec.UNKNOWN,
|
||||||
|
tags={"language": "ger"},
|
||||||
|
)
|
||||||
|
context = {"logger": get_ffx_logger()}
|
||||||
|
updated_track = original_track.clone(context=context)
|
||||||
|
updated_track.getTags()["language"] = "eng"
|
||||||
|
|
||||||
|
screen = object.__new__(MediaEditScreen)
|
||||||
|
screen.context = context
|
||||||
|
screen._sourceMediaDescriptor = MediaDescriptor(
|
||||||
|
context=context,
|
||||||
|
track_descriptors=[original_track],
|
||||||
|
)
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
screen.setMessage = lambda _message: calls.append("setMessage")
|
||||||
|
screen.refreshAfterDraftChange = lambda: calls.append("refreshAfterDraftChange")
|
||||||
|
|
||||||
|
screen.handle_edit_track(updated_track)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
"eng",
|
||||||
|
screen._sourceMediaDescriptor.getTrackDescriptors()[0].getTags()["language"],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
["setMessage", "refreshAfterDraftChange"],
|
||||||
|
calls,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_media_edit_screen_screen_resume_refreshes_draft_tables(self):
|
||||||
|
screen = object.__new__(MediaEditScreen)
|
||||||
|
screen.tracksTable = FakeTagTable()
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
screen.refreshAfterDraftChange = lambda: calls.append("refreshAfterDraftChange")
|
||||||
|
screen.updateToggleButtons = lambda: calls.append("updateToggleButtons")
|
||||||
|
|
||||||
|
screen.on_screen_resume(None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
["refreshAfterDraftChange", "updateToggleButtons"],
|
||||||
|
calls,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_pattern_details_screen_screen_resume_refreshes_tables(self):
|
||||||
|
screen = object.__new__(PatternDetailsScreen)
|
||||||
|
screen.tracksTable = FakeTagTable()
|
||||||
|
screen.tagsTable = FakeTagTable()
|
||||||
|
screen.shiftedSeasonsTable = FakeTagTable()
|
||||||
|
screen._PatternDetailsScreen__pattern = object()
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
screen.updateTags = lambda: calls.append("updateTags")
|
||||||
|
screen.updateTracks = lambda: calls.append("updateTracks")
|
||||||
|
screen.updateShiftedSeasons = lambda: calls.append("updateShiftedSeasons")
|
||||||
|
|
||||||
|
screen.on_screen_resume(None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
["updateTags", "updateTracks", "updateShiftedSeasons"],
|
||||||
|
calls,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_inspect_details_screen_handle_edit_pattern_refreshes_even_without_result(self):
|
||||||
|
screen = object.__new__(InspectDetailsScreen)
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
screen.reloadProperties = lambda reset_draft=True: calls.append(
|
||||||
|
("reloadProperties", reset_draft)
|
||||||
|
)
|
||||||
|
screen._currentPattern = None
|
||||||
|
screen.updateMediaTags = lambda: calls.append("updateMediaTags")
|
||||||
|
screen.updateTracks = lambda: calls.append("updateTracks")
|
||||||
|
screen.updateDifferences = lambda: calls.append("updateDifferences")
|
||||||
|
|
||||||
|
screen.handle_edit_pattern(None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
("reloadProperties", True),
|
||||||
|
"updateMediaTags",
|
||||||
|
"updateTracks",
|
||||||
|
"updateDifferences",
|
||||||
|
],
|
||||||
|
calls,
|
||||||
|
)
|
||||||
|
|
||||||
def test_pattern_details_screen_reads_selected_shifted_season_from_row_mapping(self):
|
def test_pattern_details_screen_reads_selected_shifted_season_from_row_mapping(self):
|
||||||
screen = object.__new__(PatternDetailsScreen)
|
screen = object.__new__(PatternDetailsScreen)
|
||||||
screen.shiftedSeasonsTable = FakeTagTable()
|
screen.shiftedSeasonsTable = FakeTagTable()
|
||||||
@@ -565,6 +664,127 @@ class TagTableScreenStateTests(unittest.TestCase):
|
|||||||
self.assertNotIn(placeholder_key, screen._showRowData)
|
self.assertNotIn(placeholder_key, screen._showRowData)
|
||||||
self.assertEqual(0, screen.getRowIndexFromShowId(8))
|
self.assertEqual(0, screen.getRowIndexFromShowId(8))
|
||||||
|
|
||||||
|
def test_inspect_details_screen_update_tracks_shows_target_pattern_tracks(self):
|
||||||
|
source_track = TrackDescriptor(
|
||||||
|
index=1,
|
||||||
|
source_index=1,
|
||||||
|
sub_index=0,
|
||||||
|
track_type=TrackType.SUBTITLE,
|
||||||
|
codec_name=TrackCodec.UNKNOWN,
|
||||||
|
tags={"language": "ger", "title": "German Full"},
|
||||||
|
)
|
||||||
|
target_track = TrackDescriptor(
|
||||||
|
index=1,
|
||||||
|
source_index=1,
|
||||||
|
sub_index=0,
|
||||||
|
track_type=TrackType.SUBTITLE,
|
||||||
|
codec_name=TrackCodec.UNKNOWN,
|
||||||
|
tags={"language": "eng", "title": "English Full"},
|
||||||
|
)
|
||||||
|
|
||||||
|
screen = object.__new__(InspectDetailsScreen)
|
||||||
|
screen.tracksTable = FakeTagTable()
|
||||||
|
screen._sourceMediaDescriptor = FakeMediaDescriptor([source_track])
|
||||||
|
screen._targetMediaDescriptor = FakeMediaDescriptor([target_track])
|
||||||
|
screen._currentPattern = object()
|
||||||
|
screen._trackRowData = {}
|
||||||
|
screen._applyNormalization = False
|
||||||
|
|
||||||
|
screen.updateTracks()
|
||||||
|
|
||||||
|
self.assertIn("English Full", screen.tracksTable.rows["row-0"])
|
||||||
|
self.assertIs(target_track, screen.getSelectedTrackDescriptor())
|
||||||
|
|
||||||
|
def test_inspect_details_screen_maps_target_selection_back_to_source_track(self):
|
||||||
|
source_track = TrackDescriptor(
|
||||||
|
index=3,
|
||||||
|
source_index=7,
|
||||||
|
sub_index=1,
|
||||||
|
track_type=TrackType.SUBTITLE,
|
||||||
|
codec_name=TrackCodec.UNKNOWN,
|
||||||
|
tags={"language": "ger"},
|
||||||
|
)
|
||||||
|
target_track = TrackDescriptor(
|
||||||
|
index=1,
|
||||||
|
source_index=7,
|
||||||
|
sub_index=0,
|
||||||
|
track_type=TrackType.SUBTITLE,
|
||||||
|
codec_name=TrackCodec.UNKNOWN,
|
||||||
|
tags={"language": "eng"},
|
||||||
|
)
|
||||||
|
|
||||||
|
screen = object.__new__(InspectDetailsScreen)
|
||||||
|
screen.tracksTable = FakeTagTable()
|
||||||
|
screen._sourceMediaDescriptor = FakeMediaDescriptor([source_track])
|
||||||
|
screen._targetMediaDescriptor = FakeMediaDescriptor([target_track])
|
||||||
|
screen._currentPattern = object()
|
||||||
|
screen._trackRowData = {}
|
||||||
|
screen._applyNormalization = False
|
||||||
|
|
||||||
|
screen.updateTracks()
|
||||||
|
|
||||||
|
self.assertIs(source_track, screen.getTrackEditSourceDescriptor())
|
||||||
|
|
||||||
|
def test_inspect_details_screen_action_update_pattern_uses_existing_change_set_before_reload(self):
|
||||||
|
class _FakePattern:
|
||||||
|
def getPattern(self):
|
||||||
|
return r"demo_(s[0-9]+e[0-9]+)\.mkv"
|
||||||
|
|
||||||
|
def getId(self):
|
||||||
|
return 9
|
||||||
|
|
||||||
|
class _FakeTagController:
|
||||||
|
def __init__(self, calls):
|
||||||
|
self._calls = calls
|
||||||
|
|
||||||
|
def deleteMediaTagByKey(self, pattern_id, key):
|
||||||
|
self._calls.append(("deleteMediaTagByKey", pattern_id, key))
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
screen = object.__new__(InspectDetailsScreen)
|
||||||
|
screen._currentPattern = _FakePattern()
|
||||||
|
screen._mediaChangeSetObj = {
|
||||||
|
"tags": {
|
||||||
|
DIFF_ADDED_KEY: {"TITLE": "Demo"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
screen._tac = _FakeTagController(calls)
|
||||||
|
screen._tc = type(
|
||||||
|
"_FakeTrackController",
|
||||||
|
(),
|
||||||
|
{
|
||||||
|
"addTrack": staticmethod(lambda *_args, **_kwargs: None),
|
||||||
|
"deleteTrack": staticmethod(lambda *_args, **_kwargs: None),
|
||||||
|
"setDispositionState": staticmethod(lambda *_args, **_kwargs: None),
|
||||||
|
},
|
||||||
|
)()
|
||||||
|
screen._sourceMediaDescriptor = FakeMediaDescriptor([], tags={})
|
||||||
|
screen._targetMediaDescriptor = FakeMediaDescriptor([])
|
||||||
|
screen.getPatternObjFromInput = lambda: {
|
||||||
|
"show_id": 1,
|
||||||
|
"pattern": r"demo_(s[0-9]+e[0-9]+)\.mkv",
|
||||||
|
}
|
||||||
|
screen.reloadProperties = lambda reset_draft=True: calls.append(
|
||||||
|
("reloadProperties", reset_draft)
|
||||||
|
)
|
||||||
|
screen.updateMediaTags = lambda: calls.append("updateMediaTags")
|
||||||
|
screen.updateTracks = lambda: calls.append("updateTracks")
|
||||||
|
screen.updateDifferences = lambda: calls.append("updateDifferences")
|
||||||
|
|
||||||
|
screen.action_update_pattern()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
("deleteMediaTagByKey", 9, "TITLE"),
|
||||||
|
("reloadProperties", True),
|
||||||
|
"updateMediaTags",
|
||||||
|
"updateTracks",
|
||||||
|
"updateDifferences",
|
||||||
|
],
|
||||||
|
calls,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user