Skip to content
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

Chapters #531

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
25 changes: 23 additions & 2 deletions eyed3/id3/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import tempfile
import textwrap

from ..utils import requireUnicode, chunkCopy, datePicker, b
from ..utils import requireUnicode, requireBytes, chunkCopy, datePicker, b
from .. import core
from ..core import TXXX_ALBUM_TYPE, TXXX_ARTIST_ORIGIN, ALBUM_TYPE_IDS, ArtistOrigin
from .. import Error
Expand Down Expand Up @@ -1814,11 +1814,32 @@ def get(self, email):

class ChaptersAccessor(AccessorBase):
def __init__(self, fs):
self._next_id = 0

def match_func(frame, element_id):
return frame.element_id == element_id
super().__init__(frames.CHAPTER_FID, fs, match_func)

def set(self, element_id, times, offsets=(None, None), sub_frames=None):
@property
def chapter_ids(self):
return tuple([chap.element_id for chap in self])

@property
def next_chapter_id(self):
curr_ids = self.chapter_ids
next_id = None

while not next_id:
self._next_id += 1
chap_id = f"ch{self._next_id}".encode("latin1")

if chap_id not in curr_ids:
next_id = chap_id

return next_id

@requireBytes(1, "element_id")
def set(self, element_id: bytes, times: tuple, offsets: tuple = (None, None), sub_frames=None):
flist = self._fs[frames.CHAPTER_FID] or []
for chap in flist:
if chap.element_id == element_id:
Expand Down
40 changes: 39 additions & 1 deletion eyed3/plugins/classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from eyed3.utils.console import (
printMsg, printError, printWarning, boldText, getTtySize,
)
from eyed3.id3.frames import ImageFrame
from eyed3.id3.frames import ImageFrame, StartEndTuple
from eyed3.mimetype import guessMimetype

from eyed3.utils.log import getLogger
Expand Down Expand Up @@ -162,6 +162,14 @@ def CommentArg(arg):
lang = vals[2] if len(vals) > 2 else id3.DEFAULT_LANG
return text, desc, b(lang)[:3]

def ChapterArg(arg):
try:
start, end, title = _splitArgs(arg)
except Exception:
raise ValueError("Invalid chapter argument")

return int(start), int(end), title

def LyricsArg(arg):
text, desc, lang = CommentArg(arg)
try:
Expand Down Expand Up @@ -330,6 +338,11 @@ def PopularityArg(arg):
dest="remove_all_comments",
help=ARGS_HELP["--remove-all-comments"])

# chapters
gid3.add_argument("--add-chapter", action="append", dest="chapters",
metavar="START:END:TITLE", default=[],
type=ChapterArg, help=ARGS_HELP["--add-chapter"])

gid3.add_argument("--add-lyrics", action="append", type=LyricsArg,
dest="lyrics", default=[],
metavar="LYRICS_FILE[:DESCRIPTION[:LANG]]",
Expand Down Expand Up @@ -711,6 +724,13 @@ def printTag(self, tag):
printMsg("\nTerms of Use (%s): %s" % (boldText("USER"),
tag.terms_of_use))

# CHAP
if tag.chapters is not None:
for chapter in tag.chapters:
printMsg(f"[{boldText('Chapter:')} {chapter.element_id} "
f"Start time: {chapter.times.start} End time: {chapter.times.end}, "
f"Title: {chapter.title}]")

# --verbose
if self.args.verbose:
printMsg(self._getHardRule(self.terminal_width))
Expand Down Expand Up @@ -906,6 +926,15 @@ def _checkNumberedArgTuples(curr, new):
accessor.set(text, desc, b(lang))
retval = True

# --add-chapter
for index, (start, end, title) in enumerate(self.args.chapters, 1):
elem_id = tag.chapters.next_chapter_id
printWarning(f"Setting chapter '{title}': {start:d} {end:d}")
tag.chapters.set(element_id=elem_id, times=StartEndTuple(start, end))
chapter = tag.chapters.get(elem_id)
chapter.title = title
retval = True

# --play-count
playcount_arg = self.args.play_count
if playcount_arg:
Expand Down Expand Up @@ -1106,6 +1135,15 @@ def _getTemplateKeys():
"--write-images": "Causes all attached images (APIC frames) to be "
"written to the specified directory.",

# chapters
"--add-chapter":
"Add a chapter. There may be more than one chapter in a "
"tag, as long as the START and END values are unique. "
"Start and end times is milliseconds offset from start. "
"End as well as TITLE are optional. If TITLE should be "
"given without END, use 4294967295 for END",


"--add-object": "Add or replace an object. There may be more than one "
"object in a tag, as long as the DESCRIPTION values "
"are unique. The default DESCRIPTION is ''.",
Expand Down