Refreshed the bundled reference datasets (trakt_networks, trakt_genres,
trakt_languages, trakt_countries, trakt_certifications) from the API.
trakt_networks no longer contains blank or duplicate network names, so
looking up networks and filtering by networks is more reliable.
New movies_updates() and shows_updates() return media recently updated on
trakt.tv since a start_date (default yesterday). The trakt.tv API only
serves updates from the last 30 days; older start_dates warn. These
endpoints do not support filters.
New filter builders filters_movies(), filters_shows(), and
filters_episodes() assemble a validated trakt_filters object to pass to
the filters argument of the dynamic-list functions (movies_popular(),
shows_trending(), ...) and search_query(). This centralizes and documents
filtering, adds the
filters the trakt.tv API gained over time ā subgenres, studio_ids,
votes, tmdb_ratings/tmdb_votes, imdb_ratings/imdb_votes,
rt_meters, rt_user_meters, metascores, network_ids, episode_types ā
and expands the status vocabulary to all eight API values. Values are
validated up front (unknown vocabulary warns and is dropped; out-of-range
numbers warn and are ignored). (#36)
Passing filters as individual arguments to the dynamic-list functions and
search_query() (genres, years, networks, ...) is soft-deprecated in
favour of the
filters argument. The individual arguments keep working for now; if both a
filters object and individual arguments are supplied, filters wins and a
warning is emitted.
lists_popular() / lists_trending() gain a type argument ("personal"
or "official", default "personal") and now send it as a required path
segment. The trakt.tv API changed: the bare lists/popular / lists/trending
endpoints now return an empty HTTP 204 response, and the list type must be
specified explicitly (lists/popular/:type). Previously these functions
silently returned an empty tibble.networks (and other fixed-vocabulary) filter validation
used by the dynamic-list functions (shows_anticipated(), movies_popular(),
etc.): a filter value that matched several entries differing only in case or
duplicated in the API's lookup data (e.g. "Netflix" / "NETFLIX") aborted
with "Result must be length 1, not 2". Such values now resolve to a single
canonical spelling.trakt_get() now retries transient server errors (HTTP 500, 502, 503, 504)
and connection failures, not just the httr2 defaults (429, 503). The
trakt.tv API intermittently returns gateway errors (502/504) that succeed on
a retry; previously these surfaced as hard failures.trakt_api_available(): a lightweight, non-erroring check of whether the
API is reachable (returns TRUE/FALSE). It is used to guard runnable
documentation examples so they execute when the API is up but are skipped
(rather than failing) during outages or when offline.@examplesIf trakt_api_available() instead of being hidden
in \dontrun{}. They are exercised by R CMD check and example() when the
API is reachable, and skipped otherwise, so upstream flakiness no longer
produces spurious example failures. Examples that genuinely require
authentication remain in \dontrun{}.re_record_interval
of 30 days caused CI to make live API calls (and fail on any upstream
hiccup or drift) once cassettes aged past the interval. Re-record cassettes
deliberately and locally instead.Getting Started, Show Analysis: 24) are now pre-computed:
they are authored in vignettes/*.Rmd.orig (with live API code) and knitted
into static vignettes/*.Rmd via vignettes/precompile.R. The shipped
vignettes contain baked-in output and make no API calls at check/build time,
so an upstream outage no longer breaks R CMD check or the pkgdown site.
Regenerate them deliberately with make vignettes (or
Rscript vignettes/precompile.R).guest_stars argument of shows_people() / seasons_people() /
episodes_people() is now deprecated (via lifecycle::deprecate_warn()).
The trakt.tv API stopped returning a separate guest_stars array ā guest
cast is now included in cast. The argument is a no-op pending removal
in a future release.movies_related() / shows_related(): when called
with multiple IDs, the internal recursion forwarded the extended argument
positionally as limit, producing malformed ?limit=min URLs and empty
responses for non-first IDs (subsequently erroring in column-flattening).seasons_ratings() adapts to the new multi-source rating response shape.
The API now returns separate sub-objects per source
(trakt, tmdb, imdb, metascore, rotten_tomatoes).
The trakt source is promoted to the top level (preserving the existing
rating / votes / distribution columns), and external scores are
surfaced as <source>_<field> columns (tmdb_rating, imdb_link,
metascore_rating, rotten_tomatoes_state, etc.). Missing values are
returned as NA.unpack_user() now renames the user's own trakt id to user_trakt to
avoid colliding with the media or list trakt id when an unpacked user
is bind_cols()'d alongside other identifiers. Previously this produced
auto-repaired column names such as trakt...17. Affects all functions
that embed user data: *_lists(), user_lists(), user_list(),
user_comments(), user_summary(), comments, media_watching(), and
list-popular endpoints.user_list() no longer errors on single-list responses where the embedded
user profile contains NULL fields (e.g. age, vip_cover_image); these
are now coerced to NA before tibble conversion.shows_people() / seasons_people() / episodes_people() no longer
return a separate guest_stars table when called with guest_stars = TRUE,
because the trakt API stopped returning that array. Guest cast is now
mixed into cast. The guest_stars argument is currently a no-op pending
a deprecation decision.plex_guid and plex_slug ID columns, subgenres as a list column, and new fields tagline and original_title.images and colors objects are dropped from show data as they don't fit tabular output.search_query() and search_id() now return properly structured show data for both extended = "min" and extended = "full".seasons_season() was renamed to seasons_episodes(), wrapping endpoint shows/:id/seasons/:season?translations=.seasons_season() now wraps new(?) endpoint shows/:id/seasons/:season/info.{testthat} dependency to >=3.0.0, which had been necessary since adopting 3e.{purrr} usage
~ .x with base-R \(x) x-style onesmap_df() with map() + list_rbind()extended parameter across all API functions:
"min" no longer sends ?extended=min to the API (which was never a valid API value). Instead, the query parameter is omitted entirely, matching the API's documented behavior."images", "metadata", and combinations like c("full", "images") or "full,images" are now supported.extended includes "images", image data is preserved as a list-column instead of being dropped.validate_extended() internally instead of match.arg(), providing clearer error messages for invalid values.This should have been a small under the hood change, but it also enabled major changes in the way I'm handling API secrets, allowing me to include my encrypted client secret with the package directly. This should make it more convenient for users to make authenticated requests without having to register their own app on trakt.tv.
The main user-facing highlight however should probably be more stable API interactions including
getOption("tRakt_cache_dir"), which is pruned
tRakt_cache_max_age)tRakt_cache_max_size)magrittr import and use |> internally, hence bumping the R dependency to >= 4.1.tRakt_sitrep() is now available to check general settings and API credentials.{testthat} 3e.usethis::use_github_actions()usethis::use_tidy_ci(), this bumps the R dependency to >= 3.2.By default, all requests to the API are now made using authentication if a trakt_client_secret is set. See ?trakt_credentials() for details on how to set your credentials.
If no secret is available, unauthenticated requests are made as in previous versions. In this case, you still need either the built-in or your own trakt_client_id.
OAuth2 support comes with the need to handle secrets, i.e. .httr-oauth files. This can be a pain with headless/automated setups, which is why I am trying to keep "no auth needed" as the default, as most (implemented) endpoints don't strictly require authentication.
See vignette("Implemented-API-methods", package = "tRakt") for an overview of implemented methods with an indicator on whether authentication is required (it usually isn't).
episodes_summary() [shows/:id/seasons/:season/episodes/:episode]: What seasons_season() and seasons_summary() do, but for a single episode. Only really implemented for completeness' sake. Wraps this method.user_profile() [user/:id]: Get a user's profile.user_lists() [users/:id/lists]: Get all user lists.user_list() [users/:id/lists/:list_id]: Get a single list.user_list_items() [users/:id/lists/:list_id/items/:type]: Get the items in said lists.Rename all the things to snake_case and drop the trakt. prefix.
This results in (most) functions mapping directly to API endpoints in the form section_method,
e.g. the API endpoint movies/collected is handled by function movies_collected().
trakt.search -> search_query()trakt.search.byid -> search_id()trakt.popular
movies_popular()shows_popular()trakt.trending
movies_trending()shows_trending()trakt.anticipated
movies_anticipated()shows_anticipated()trakt.played
movies_played()shows_played()trakt.watched
movies_watched()shows_watched()trakt.collected()
movies_collected()shows_collected()trakt.updates()
movies_updates()shows_updates()trakt.movies.summary -> movies_summary()trakt.movies.boxoffice -> movies_boxoffice()trakt.movies.releases -> movies_releases()trakt.movies.people -> movies_people()trakt.shows.summary -> shows_summary()trakt.shows.people -> shows_people()trakt.seasons.summary -> seasons_summary()trakt.seasons.season -> seasons_season() (It's silly, I know, but oh well)trakt.movies.ratings -> movies_ratings()trakt.shows.ratings -> shows_ratings()trakt.seasons.ratings -> seasons_ratings()trakt.episodes.ratings -> episodes_ratings()trakt.movies.related -> movies_related()trakt.shows.related -> shows_related()trakt.movies.stats -> movies_stats()trakt.shows.stats -> shows_stats()trakt.seasons.stats -> seasons_stats()trakt.episodes.stats -> episodes_stats()trakt.people.summary -> people_summary()trakt.people.movies -> people_movies()trakt.people.shows -> people_shows()trakt.user.network
user_followers()user_following()user_friends()trakt.user.collection -> user_collection()trakt.user.history -> user_history()trakt.user.ratings -> user_ratings()trakt.user.stats -> user_stats()trakt.user.watched -> user_watched()trakt.user.watchlist -> user_watchlist()This is a big one, but not 1.0.0 big one I guess.
This is a consolidation release with lots of internal improvements, but for the 1.0.0 I decided to overhaul the structure of the package again, so before I completely break any kind of backwards compatiblity, I thought I'd get this one out.
Major refactor of the docs. Sadly, the user will only notice a little more consistency.
Consolidate related functions into the same Rd page
Centrally document common parameters and reuse them where needed
Also consolidate many similar functions by factoring out the relevant bits into more flexible helper functions. In some cases those are exported as well.
In the process, some functions where partially renamed (.show. -> .shows., .movie. -> .movies.) for consistency.
Rename get_trakt_credentials -> trakt_credentials
Many functions now return much flatter output, notably *people* functions. They return a list with cast and crew objects, which in turn are both flat tibbles.
trakt.search).
year that is NA but a search score == 1000, that's probably bad and dropped.trakt.user.history for user/:id/history methodsMany functions were refactored, which in some cases changes the output.
Not only do most functions return a tibble now, but some additionally computed variables have been removed as well.
usethis::use_* all the things, basically.pkgdown site and a README.Rmdcodecovstyler::style_pkg()trakt.getEpisodeData -> trakt.get_all_episodes
season_nums argument to make it easier to get all episodes of a show.trakt.getFullShowData -> trakt.get_full_showdatatrakt.seasons.summarydropunaired param to trakt.seasons.summary (defaults to TRUE).
extended to be more than min since the required aired_episodes field
is only present with higher levels of detail.extended param to trakt.user.f*trakt.user.f* now return NULL when the user is private instead of failing.trakt.*.populartrakt.*.relatedtrakt.*.trendingtrakt.*.summarytrakt.*.ratingstrakt.user.f* functions: Results will be rbinded together and a source_user column is appended.trakt.seasons.season: Soon to make trakt.getEpisodeData obsolete.trakt.*.summary: Forces force_data_frame to TRUE to enable rbinding.trakt.*.related: Appends source column containing respective input id.trakt.*.ratings: Returns result as a list with each entry containing data for each
show or movie to accommodate the distribution variable (data.frame)trakt.people.summary: Appends person column with target id.trakt.*.watching: Returns data.frame with source column containing each target.build_trakt_url to ease trakt API URL assembly and reduce duplicate code.force_data_frame option to trakt.*.summary: Forces unnesting.trakt.show.stats to trakt.stats, will work with both movies and shows
as soon as the API endpoint actually works.@family tags in docs a littletrakt.movie.watching and trakt.show.watching: Get trakt.tv users watching.trakt.movie.releases: Gets release dates & certifications per movie.trakt.search.byid: Used to only work on shows, now actually works on movies.fix_datetime (internal): improve reliability.*.movie.* functions.extended param to trakt.user.watchlisttrakt.user.watched and trakt.user.collection
slug to id.slugtrakt.user.watchlist when type = shows was ignored by accidenttrakt.getSeasons -> trakt.seasons.summary for consistency with the trakt API.trakt.show.season -> trakt.seasons.season for consistency with the trakt API.getNameFromUrl -> parse_trakt_url to be more descriptive. Also, de-camelCasezation.people functions:
trakt.people.summarytrakt.people.moviestrakt.people.showstrakt.show.people and trakt.movie.peopleextended argument to trakt.movies.related and trakt.shows.related (defaults to min)page param to paginated functions:
trakt.shows.populartrakt.shows.trendingtrakt.movies.populartrakt.movies.trendingtrakt.user.stats:
to.data.frame option because the output is too messy@family tag to docs for aggregation functions (.popular, .trending, .related)trakt.api.call's job.trakt.getEpisodeData:
NA firstaired fieldsepisode_abs field is usually NA, so let's dump epnum on itextended = "min" as default across functions for consistency with the trakt APIyear parameter to trakt.searchquery in trakt.search ends with a 4 digit number, this will be used as year parameter and stripped from the original querytestthat testslubridate::parse_date_time fails, as.POSIXct is used as a fallbacktrakt.movies.popular: Analogous to trakt.shows.populartrakt.movies.trending: Analogous to trakt.shows.trendingtrakt.movie.summary: Get a single movie's details, analogous to trakt.show.summarytrakt.movies.related: Get related moviestrakt.movie.people: Analogous to trakt.show.peopletrakt.show.ratings and trakt.movie.ratings to receive just the ratings and distribution for a single show or movietrakt.show.related -> trakt.shows.related for consistency with .trending and .populartarget params in accordance with changed trakt API docstrakt.user.ratings: Currently supported types: shows, movies, episodesextended option to trakt.shows.popular and .trending.posix have been removed and the existing date fiels are now converted to POSIXct to remove clutteringfirstaired.posix -> first_aired etctrakt.user.following / .followers / .friends) because maybe I want to throw networkD3 at my people.posix variables should be removed and the existing date variables should just be properly converted to POSIXct@family tags to all functions to group them togetherI've been working on some user-specific methods, so you can now use the trakt.user.* family of functions to get a user'sā¦
trakt.user.collection()trakt.user.watched()trakt.user.stats()All of them default to the username set in getOption("trakt.username"), but any publicly viewable user should work. Note that OAuth2 is not implemented, so private users can't be accessed.
As of today, all the functions are updated to use the new APIv2, except for trakt.show.stats,
which is currently not yet implemented at trakt.tv, see their docs
Now the package is usable again, and I can continue to work on bug fixes and enhancements. Yay.
I am now trying to migrate everything to the new trakt.tv APIv2.
Since I mostly don't know what I'm doing, I have to make this up as I go along, but oh well.
Now the search function trakt.search should be working fine, as it is the only function
that is tested/built with the new API in mind. Others to come.