diff --git a/.Rbuildignore b/.Rbuildignore index ecd06c4..075363e 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -8,3 +8,5 @@ ^synced_episode.srt/ ^cran-comments\.md$ ^codemeta\.json$ +^CRAN-SUBMISSION$ +^tests.R$ diff --git a/.gitignore b/.gitignore index dc92163..fdb087a 100644 --- a/.gitignore +++ b/.gitignore @@ -93,3 +93,5 @@ $RECYCLE.BIN/ /.quarto/ **/*.quarto_ipynb +CRAN-SUBMISSION +README.html diff --git a/DESCRIPTION b/DESCRIPTION index 01497b0..385287a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: subtools Type: Package Title: Read and Manipulate Video Subtitles Version: 1.1.0 -Date: 2026-03-11 +Date: 2026-03-16 Authors@R: c( person("Francois", "Keck", email = "francois.keck@gmail.com", role = c("aut", "cre", "cph"), @@ -13,9 +13,9 @@ Authors@R: c( comment = c(ORCID = "0000-0002-3827-1063")) ) Maintainer: Francois Keck -Description: A collection of functions to read, write and manipulate video subtitles. - Supported formats include 'srt', 'subrip', 'sub', 'subviewer', 'microdvd', - 'ssa', 'ass', 'substation', 'vtt', and 'webvtt'. +Description: A collection of functions to read, write and manipulate video + subtitles. Supported formats include 'srt', 'subrip', 'sub', 'subviewer', + 'microdvd', 'ssa', 'ass', 'substation', 'vtt', and 'webvtt'. Language: en-GB License: GPL-3 LazyData: TRUE @@ -40,3 +40,5 @@ Config/testthat/edition: 3 Config/testthat/parallel: false Config/testthat/start-first: *assert*, *utils*, *read* RoxygenNote: 7.3.3 +X-schema.org-applicationCategory: Text Processing +X-schema.org-keywords: subtitles, video, text, srt, vtt, ssa, natural language processing diff --git a/R/assert_objects.R b/R/assert_objects.R index 7bb219f..1188237 100644 --- a/R/assert_objects.R +++ b/R/assert_objects.R @@ -1,7 +1,15 @@ -# .assert_subtitles -# .extract_metadata - -# Sanity check for subtitles objects +#' Validate a subtitles object +#' +#' Checks that \code{x} inherits from both \code{"subtitles"} and \code{"data.frame"}, +#' has the four required columns (\code{ID}, \code{Timecode_in}, \code{Timecode_out}, +#' \code{Text_content}), and that the two timecode columns are of class \code{"hms"}. +#' Stops with an informative error message if any check fails. +#' +#' @param x an object to validate. +#' +#' @returns Called for its side effects (validation). Returns \code{NULL} invisibly. +#' +#' @noRd .assert_subtitles <- function(x) { if (!is(x, "subtitles")) { stop("A subtitles object must inherit from class \"subtitles\".") @@ -35,7 +43,17 @@ } } -#extract metadata columns from a subtitles object +#' Extract metadata columns from a subtitles object +#' +#' Returns all columns that are not part of the core subtitle structure +#' (\code{ID}, \code{Timecode_in}, \code{Timecode_out}, \code{Text_content}). +#' +#' @param x a \code{subtitles} object. +#' +#' @returns A data frame (or tibble) containing only the metadata columns of \code{x}. +#' If there are no metadata columns, an empty data frame is returned. +#' +#' @noRd extract_metadata <- function(x) { sub_names <- c("ID", "Timecode_in", "Timecode_out", "Text_content") md_names <- setdiff(colnames(x), sub_names) diff --git a/R/read_subtitles.R b/R/read_subtitles.R index cdc172b..6cf7028 100644 --- a/R/read_subtitles.R +++ b/R/read_subtitles.R @@ -14,6 +14,9 @@ #' @param encoding the name of the encoding to be used. Default is "\code{auto}" and #' uses \code{\link[readr]{guess_encoding}} to detect encoding. #' @param ... passed on to downstream methods. +#' +#' @returns An object of class \code{subtitles} (see \code{\link{subtitles}}). +#' #' @importFrom utils read.csv #' @export #' @examples @@ -431,6 +434,8 @@ subtitles <- function( #' @param printlen the maximum number of subtitles to print. #' @param ... further arguments passed to or from other methods. #' +#' @returns Called for its side effects (printing to the console). Returns \code{NULL} invisibly. +#' #' @examples #' f <- system.file("extdata", "ex_subrip.srt", package = "subtools") #' s <- read_subtitles(f) @@ -455,6 +460,8 @@ print.multisubtitles <- function(x, printlen = 10L, ...) { #' #' @param x a \code{subtitles} or \code{multisubtitles} object. #' +#' @returns Called for its side effects (printing to the console). Returns \code{NULL} invisibly. +#' #' @examples #' s <- read_subtitles( #' system.file("extdata", "ex_subrip.srt", package = "subtools") diff --git a/R/unnest_tokens.R b/R/unnest_tokens.R index 97c96e7..ca2c505 100644 --- a/R/unnest_tokens.R +++ b/R/unnest_tokens.R @@ -162,6 +162,9 @@ unnest_tokens.subtitles <- function( #' The default method (\code{unnest_tokens.default}) delegates to the original implementation. #' See "?unnest_tokens.subtitles" for the \code{subtools} specific documentation. #' @inheritParams tidytext::unnest_tokens +#' +#' @returns A tibble. +#' #' @export unnest_tokens <- function( tbl, diff --git a/R/utils_regexes.R b/R/utils_regexes.R index 3d2fe82..08afea1 100644 --- a/R/utils_regexes.R +++ b/R/utils_regexes.R @@ -1,11 +1,28 @@ -# Extract file name from dir paths +#' Extract the file name from a path +#' +#' Strips trailing slashes and returns the last path component. +#' +#' @param x a character vector of file or directory paths. +#' +#' @returns A character vector of file names, the same length as \code{x}. +#' +#' @noRd .extr_filename <- function(x) { x <- gsub("/+$", "", x) x <- regmatches(x, regexpr("([^/]+$)", x)) return(x) } -# Extract extension from file name +#' Extract the extension from a file name +#' +#' Returns the part of the file name after the last dot (lower-case alphanumeric only). +#' +#' @param x a character vector of file names or paths. +#' +#' @returns A character vector of file extensions (without the leading dot), +#' the same length as \code{x}. +#' +#' @noRd .extr_extension <- function(x) { x <- sub("[?#].*$", "", x) is_url <- grepl("^[[:alpha:]][[:alnum:].+-]*://", x) @@ -90,7 +107,18 @@ return(res) } -# Extract Episode number +#' Extract episode number from file paths +#' +#' Parses the file name component of each path and attempts to detect the episode +#' number using four common naming conventions: +#' \code{E01}, \code{EPISODE.2}, \code{S03E05}, and \code{2x05}. +#' Returns \code{NA} for paths where no convention matches. +#' +#' @param x a character vector of file or directory paths. +#' +#' @returns A numeric vector of episode numbers, the same length as \code{x}. +#' +#' @noRd .extr_enum <- function(x) { x <- .extr_filename(x) x <- toupper(x) @@ -129,7 +157,17 @@ } -# For WebVTT parsing: select cue blocks only (drop REGION, STYLE, NOTE) +#' Test whether a string is a WebVTT cue timing line +#' +#' Used during WebVTT parsing to identify cue blocks and filter out +#' \code{REGION}, \code{STYLE}, and \code{NOTE} blocks. +#' +#' @param x a single character string (or \code{NA}). +#' +#' @returns A single logical: \code{TRUE} if \code{x} matches the WebVTT cue timing +#' pattern, \code{FALSE} otherwise (including when \code{x} is \code{NA}). +#' +#' @noRd .test_cuetiming <- function(x) { if (is.na(x)) { res <- FALSE diff --git a/R/utils_time.R b/R/utils_time.R index e4eb48f..ae51373 100644 --- a/R/utils_time.R +++ b/R/utils_time.R @@ -1,4 +1,14 @@ -# Format time as HH:MM:SS.mS +#' Parse subtitle timecode strings into hms objects +#' +#' Accepts timecode strings in \code{HH:MM:SS.mS} or \code{MM:SS.mS} format +#' (comma as decimal separator is also accepted). Missing hour components are +#' padded to zero. +#' +#' @param x a character vector of timecode strings. +#' +#' @returns An \code{hms} vector (from the \pkg{hms} package), the same length as \code{x}. +#' +#' @noRd .format_subtime <- function(x) { x <- gsub(",", ".", x) x <- strsplit(x, split = ":") diff --git a/R/write_subtitles.R b/R/write_subtitles.R index 021edb6..8bf60bb 100644 --- a/R/write_subtitles.R +++ b/R/write_subtitles.R @@ -8,6 +8,8 @@ #' Not used (only SubRip format is currently implemented). #' @param encoding the name of the encoding to be used. #' +#' @returns Called for its side effects (writing to \code{file}). Returns \code{NULL} invisibly. +#' #' @export #' write_subtitles <- function(x, file, format = "srt", encoding = "UTF-8") { diff --git a/README.md b/README.md index 4680e0a..3e214c3 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ oss_sub <- read_subtitles("ex_OSS_117.srt") ``` r rushmore_sub #> # A tibble: 4 × 4 -#> ID Timecode_in Timecode_out Text_content -#>