Skip to content

Editing capabilities after import #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pyamll/components/carousel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from textual.widgets import Label, ListItem, ListView
from textual.containers import Horizontal, Vertical
from parser import VocalElement, Lyrics
from parser.modify import ModificationType
from enum import Enum
from utils import convert_seconds_to_format as fsec

Expand Down Expand Up @@ -139,6 +140,18 @@ def push(self, vocal_element:VocalElement, active:bool=False, first=False) -> No

if active:
self.active_item = new_item

def rebuild(self) -> None:
operation = self.app.CURR_LYRICS.modification_stack[-1]
if operation._type == ModificationType.DELETE:
self.remove_children(".active") # Remove the current active element
# If first element then go to next element
if self.active_item.element.line_index == 0 and self.active_item.element.word_index == 0:
self.move(ScrollDirection.forward)
else:
self.move(ScrollDirection.backward)
# Else go back



class VerticalScroller(ListView):
Expand Down
32 changes: 32 additions & 0 deletions pyamll/components/elementedit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from textual.app import ComposeResult
from textual.containers import Grid
from textual.screen import ModalScreen
from textual.widgets import Button, Label, Input, Checkbox
from parser import VocalElement
from parser.modify import LyricModifyOperation, ModificationType

class ElementEditModal(ModalScreen[LyricModifyOperation]):
"""Screen with a dialog to edit a VocalElement."""
def __init__(self, element:VocalElement):
self.vocal_element = element
super().__init__()

def compose(self) -> ComposeResult:
yield Grid(
Label("Edit a VocalElement", id="element_edit_label"),
Input("", id="vocal_element_text"),
Checkbox("Is Explicit?", id="is_explicit_checkbox"),
Button("Delete Element", variant="error", id="delete"),
Button("Save", variant="primary"),
Button("Cancel", id="cancel"),
id="element-edit-dialog"
)

def on_mount(self) -> None:
self.query_one(Input).value = self.vocal_element.text

def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "cancel":
self.dismiss()
elif event.button.id == "delete":
self.dismiss(LyricModifyOperation(ModificationType.DELETE, self.vocal_element))
48 changes: 40 additions & 8 deletions pyamll/parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,57 @@ def __str__(self):
class Lyrics(list):
element_map = []
init_list:list[Line]
modification_stack = []

def __init__(self, init_list:list[Line], *args):
self.init_list = init_list
for i,line in enumerate(init_list):
self.generate_element_map()

super().__init__(*args)

def generate_element_map(self) -> None:
for i,line in enumerate(self.init_list):
line:Line = line
for j, element in enumerate(line.elements):
self.element_map.append([element, i, j])
super().__init__(*args)

def get_offset_element(self, element:VocalElement, offset:int) -> VocalElement:
for i,map_item in enumerate(self.element_map):
if element == map_item[0]:
return self.element_map[i+offset][0]


def get_element_map_index(self, element:VocalElement) -> int:
for i,map_item in enumerate(self.element_map):
if element == map_item[0]:
return i
return 0

def rebuild(self) -> None:
# Check if any empty lines
del_line_list = []
del_word_list = []

for i, line in enumerate(self.init_list):
for j, word in enumerate(line.elements):
if word is None:
del_word_list.append((i,j))

# Update word_index for words after it
for word in line.elements[j+1:]:
word.word_index -=1

for i,j in del_word_list:
del self.init_list[i].elements[j]

for i,line in enumerate(self.init_list):
# check if any empty words
if not line.elements:
del_line_list.append(i)

# Update line_index of all the elements after it
for _line in self.init_list[i+1:]:
for _elements in _line.elements:
_elements.line_index -=1

for i in del_line_list:
del self.init_list[i]

self.generate_element_map()


def process_lyrics(lyrics_str:str) -> Lyrics:
Expand Down
37 changes: 37 additions & 0 deletions pyamll/parser/modify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from enum import Enum
from parser import Lyrics, VocalElement

class ModificationType(Enum):
EDIT = "edit"
DELETE = "delete"
APPEND = "append"

class OperationStatus(Enum):
PENDING = "pending"
ONGOING = "ongoing"
EXEUTED = "executed"
FAILED = "failed"

class LyricModifyOperation():
def __init__(self,_type:ModificationType, modified_element:VocalElement ,lyrics:Lyrics|None=None) -> None:
self._type = _type
self.lyrics = lyrics
self.status:OperationStatus = OperationStatus.PENDING
self.context:str = ""
self.modified_element = modified_element

def execute(self) -> Lyrics:

if self._type == ModificationType.DELETE:
for mapping in self.lyrics.element_map:
i_element:VocalElement = mapping[0]
line = mapping[1]
word = mapping[2]
if (i_element.line_index, i_element.word_index) == (self.modified_element.line_index, self.modified_element.word_index):
break

self.lyrics.init_list[line].elements[word] = None

self.lyrics.rebuild()
self.status = OperationStatus.EXEUTED
return self.lyrics
19 changes: 16 additions & 3 deletions pyamll/screens/sync.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from components.carousel import Carousel, ScrollDirection, VerticalScroller
from components.playerbox import PlayerBox
from components.sidebar import Sidebar
from parser import Lyrics, process_lyrics
from screens.edit import EditScreen

from components.elementedit import ElementEditModal
from parser import Lyrics
from parser.modify import LyricModifyOperation, OperationStatus

from textual import events
from textual.app import ComposeResult
Expand All @@ -28,6 +28,7 @@ def compose(self) -> ComposeResult:
tooltip="Set timestamp as the end of current word and start time of the next word"),
Button("H", id="set_end_time",
tooltip="Set Timestamp as the endtime of the current word and stay there"),
Button("✎", id="edit_vocal_element", tooltip="Edit VocalElement"),
id="carousel_control"
))
yield PlayerBox(id="player_box", player=self.app.PLAYER)
Expand Down Expand Up @@ -70,6 +71,18 @@ def on_button_pressed(self, event: Button.Pressed):
carousel._nodes[active_item_index + 1].update()

carousel._nodes[active_item_index].update()

elif event.button.id == "edit_vocal_element":
def _set_element(modifier:LyricModifyOperation) -> None:
modifier.lyrics = self.app.CURR_LYRICS
self.app.CURR_LYRICS = modifier.execute()
self.app.CURR_LYRICS.modification_stack.append(modifier)
if modifier.status == OperationStatus.EXEUTED:
# Refresh carousel and vertical scroller
self.app.notify("deleted element.")
carousel.rebuild()

self.app.push_screen(ElementEditModal(carousel.active_item.element), _set_element)

def on_screen_resume(self, event: events.ScreenResume):
label: Static = self.query_one("#lyrics_label", Static)
Expand Down
38 changes: 38 additions & 0 deletions pyamll/styles/elementedit.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
ElementEditModal {
align: center middle;
}

#element-edit-dialog {
grid-size: 2;
grid-rows: 1fr 3;
border: thick $background 80%;
background: $surface;
padding: 0 1;
width: 60;
height: 20;
grid-gutter: 1 2;
}

#element_edit_label {
column-span: 2;
width: 1fr;
margin-top: 1;
content-align: center middle;
}

#vocal_element_text {
column-span: 2;
}

#is_explicit_checkbox {
column-span: 2;
width: 1fr;
}

#element-edit-dialog > Button {
width: 1fr;
}

#element-edit-dialog > #delete {
column-span: 2;
}
1 change: 1 addition & 0 deletions pyamll/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class TTMLApp(App):
"styles/sidebar.tcss",
"styles/carousel.tcss",
"styles/playerbox.tcss",
"styles/elementedit.tcss",
]

CURR_LYRICS: Lyrics = None
Expand Down