Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php
/**
* Template Name: Caption with DeltaScribe
*/

$wptv_deltascribe_css = get_template_directory() . '/plugins/wordpresstv-deltascribe/caption-with-deltascribe.css';

wp_enqueue_style(
'wptv-caption-with-deltascribe',
get_template_directory_uri() . '/plugins/wordpresstv-deltascribe/caption-with-deltascribe.css',
array( 'wptv-style' ),
file_exists( $wptv_deltascribe_css ) ? filemtime( $wptv_deltascribe_css ) : false
);

// Password-protect this form.
if ( post_password_required() ) :
get_header();
?>
<div class="wptv-hero">
<div class="single container">
<h2><?php esc_html_e( 'Caption a Video with DeltaScribe', 'wptv' ); ?></h2>
</div>
</div>

<div class="container">
<div class="video-upload">
<p>
<?php
printf(
wp_kses_post( __( 'Hey there! If you&#8217;re interested in subtitling or captioning videos for WordPress.tv, please fill out the <a href="%s">contact form</a>, and we&#8217;ll be in touch.', 'wptv' ) ),
esc_url( 'https://wordpress.tv/contact/' )
);
?>
</p>
<div class="pass-form">
<?php echo wp_kses_post( get_the_password_form() ); ?>
</div>
</div>
</div>
<?php
get_footer();
return;
endif; // post_password_required

if ( ! class_exists( 'VideoPress_Subtitles' ) ) {
wp_die( esc_html__( 'Not ready yet.', 'wptv' ) );
}

if ( empty( $_GET['video'] ) ) {
wp_die( esc_html__( 'Needs a video context.', 'wptv' ) );
}

$video_id = absint( $_GET['video'] );
if ( ! wp_attachment_is_video( $video_id ) ) {
wp_die( esc_html__( 'You can only caption videos.', 'wptv' ) );
}

$attachment = get_post( $video_id );
$parent = get_post( $attachment->post_parent );

if ( ! $parent || ! in_array( $parent->post_status, array( 'publish', 'private' ), true ) ) {
wp_die( esc_html__( 'You can not caption this video, sorry.', 'wptv' ) );
}

get_header();

$message = '';

if ( ! empty( $_REQUEST['error'] ) ) {
$error_code = (int) $_REQUEST['error'];

switch ( $error_code ) {
case 1:
$message = __( 'Error: please provide a WordPress.org username and a valid email address.', 'wptv' );
break;
case 2:
$message = __( 'Error: invalid language.', 'wptv' );
break;
case 3:
$message = __( 'Error: could not determine this video&#8217;s media URL. Please try again later.', 'wptv' );
break;
default:
$message = __( 'Unknown error. Please try again later.', 'wptv' );
break;
}
$message = '<div class="error"><p>' . esc_html( $message ) . '</p></div>';
}
?>

<div class="wptv-hero">
<div class="single container">
<h2><?php esc_html_e( 'Caption a Video with DeltaScribe', 'wptv' ); ?></h2>
</div>
</div>

<div class="container">
<div class="video-upload">
<?php echo wp_kses_post( $message ); ?>

<p>
<?php
printf(
wp_kses_post( __( 'Captioning: <a href="%1$s">%2$s</a>', 'wptv' ) ),
esc_url( get_permalink( $parent->ID ) ),
esc_html( apply_filters( 'the_title', $parent->post_title ) )
);
?>
</p>

<p><?php esc_html_e( 'DeltaScribe is a free browser-based tool for timing captions to a video. Fill out the form below, and we&#8217;ll send you over to DeltaScribe with this video already loaded — when you&#8217;re done, click Submit there to send your captions back here for review.', 'wptv' ); ?></p>

<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="deltascribe-start-form">

<?php wp_nonce_field( 'wptv-deltascribe-start', 'wptv-deltascribe-start-nonce' ); ?>
<input type="hidden" name="action" value="wptv_deltascribe_start" />
<input type="hidden" name="wptv_video_id" value="<?php echo absint( $video_id ); ?>" />

<table>
<tr>
<th><label for="wptv_wporg_username"><?php esc_html_e( 'WordPress.org Username', 'wptv' ); ?><span class="required"> * </span></label></th>
<td>
<input type="text" id="wptv_wporg_username" name="wptv_wporg_username" /><br />
<?php
printf(
wp_kses_post(
/* translators: 1: WordPress.org URL, 2: WordPress.org login URL, 3: WordPress.org registration URL */
__( 'To contribute captions, you must be a registered user at the <a href="%1$s">WordPress.org</a> website. Note that this is the username you use to log in at WordPress.org, not the username you use to log in on your own WordPress-powered site.<br />If you think you are registered but aren&#8217;t sure, you can try logging in at <a href="%2$s">login.WordPress.org</a>.<br />If you don&#8217;t have a WordPress.org username yet, you can <a href="%3$s">sign up for a free account</a>.', 'wptv' )
),
esc_url( 'https://wordpress.org' ),
esc_url( 'https://login.wordpress.org/' ),
esc_url( 'https://login.wordpress.org/register' )
);
?>
</td>
</tr>

<tr>
<th><label for="wptv_author_email"><?php esc_html_e( 'Email Address', 'wptv' ); ?><span class="required"> * </span></label></th>
<td>
<input type="text" id="wptv_author_email" name="wptv_author_email" />
</td>
</tr>

<tr>
<th><label for="wptv_language"><?php esc_html_e( 'Language', 'wptv' ); ?><span class="required"> * </span></label></th>
<td>
<select name="wptv_language" id="wptv_language">
<?php foreach ( VideoPress_Subtitles::get_languages() as $value => $language ) : ?>
<option value="<?php echo esc_attr( $value ); ?>" ><?php echo esc_html( $language['localized_label'] ); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>

<tr>
<td colspan="2"><em><?php esc_html_e( '* All fields are required', 'wptv' ); ?></em></td>
</tr>

<tr>
<td colspan="2" class="last"><input type="submit" id="wptv_deltascribe_start" value="<?php esc_attr_e( 'Continue to DeltaScribe', 'wptv' ); ?>" /></td>
</tr>
</table>
</form>
</div>

<div id="deltascribe-instructions">
<h3><?php esc_html_e( 'Instructions', 'wptv' ); ?></h3>
<p><?php esc_html_e( 'After you click Continue, DeltaScribe will open in a new page with this video already loaded. Time out your captions there, then click its Submit button to send them back to WordPress.tv for review.', 'wptv' ); ?></p>
</div>
</div>

<?php get_footer();
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function __construct() {
require_once get_template_directory() . '/plugins/wordpresstv-rest/wordpresstv-rest.php';
require_once get_template_directory() . '/plugins/wordpresstv-anon-upload/anon-upload.php';
require_once get_template_directory() . '/plugins/wordpresstv-upload-subtitles/wordpresstv-upload-subtitles.php';
require_once get_template_directory() . '/plugins/wordpresstv-deltascribe/wordpresstv-deltascribe.php';
require_once get_template_directory() . '/plugins/wordpresstv-open-graph/wordpresstv-open-graph.php';
require_once get_template_directory() . '/plugins/wordpresstv-event-meta/wordpresstv-event-meta.php';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
html,
body {
width: 100%;
height: 100%;
margin: 0;
}

#page {
height: auto;
min-height: 100%;
position: relative;
width: 100%;
}

#header {
margin: 0;
padding-top: 10px;
}

#footer {
position: absolute;
bottom: 0;
right: 0;
left: 0;
}

.noscript-show p {
margin: 0 !important;
}

.container {
overflow: hidden;
}

.video-upload h3 {
font-size: 22px;
}

.video-upload table tr th,
.video-upload table tr td {
padding: 7px 0;
line-height: 1.4em;
}

.video-upload table tr th {
width: 180px;
font-weight: bold;
}

.video-upload table tr th label .required {
line-height: 15px;
vertical-align: bottom;
margin: 0 3px;
}

.video-upload input[type="text"] {
border-radius: 3px;
border: 1px solid #dfdfdf;
color: #333;
background-color: #fff;
padding: 4px;
width: 329px;
max-width: 329px;
}

.video-upload input[type="text"]:focus {
border-color: #bbb;
}

#deltascribe-instructions {
overflow: auto;
}

#deltascribe-start-form p.last,
#deltascribe-start-form table tr td.last {
padding: 15px 0;
text-align: left;
}

#deltascribe-start-form input[type="submit"] {
font-size: 15px;
padding: 4px 12px;
}

.video-upload .pass-form label {
float: none;
display: inline;
width: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
=== DeltaScribe Captioning Integration ===

Adds a "Caption with DeltaScribe" link to video pages, sending contributors to a
DeltaScribe (https://github.com/[owner]/deltascribe) instance pre-loaded with the
video, and accepting finished captions back into the same pending-attachment
moderation queue used by wordpresstv-upload-subtitles.

== How it works ==

1. `caption-with-deltascribe-template.php` collects a WordPress.org username, email,
and language (same fields as the existing "Subtitle this video" flow).
2. `wordpresstv_deltascribe_start` (admin-post.php) validates that, builds an
HMAC-signed, expiring `submit` callback URL, and redirects out to the configured
DeltaScribe instance with `media`, `submit`, `lang`, and `format=ttml` query args.
3. DeltaScribe (a purely client-side app) loads the video, lets the contributor time
captions, and POSTs the finished TTML as JSON to the `submit` URL.
4. `wordpresstv_deltascribe_submit` (admin-post.php) verifies the HMAC token, saves
the TTML as a pending attachment, and tags it with the same `_wptv_submitted_subtitles`
postmeta shape the existing plugin uses — so it shows up in the normal Media Library
moderation screen with zero changes there.

== Configuration ==

Settings -> General -> "DeltaScribe URL" controls which DeltaScribe instance
contributors are sent to. Defaults to https://deltascribe.stephanis.me/ if unset.

== Why the DeltaScribe URL is configurable rather than bundled ==

DeltaScribe (https://github.com/[owner]/deltascribe) is a purely client-side
React/Vite app — it never uploads media or subtitles anywhere itself, and the
integration relies entirely on it being loaded in the contributor's own browser
and POSTing back to us. That means it doesn't strictly need to be hosted
separately: it could be added as a git submodule under this theme and built +
served same-origin at, say, `/deltascribe/`.

That approach was considered for this integration and intentionally deferred:

* This is an initial prototype, and the externally-hosted instance is expected
to change URLs during that phase — an admin-configurable settings field
covers that without a deploy.
* wordpress.tv's deploy pipeline (`.github/workflows/dev.yaml`) currently ships
this theme as plain PHP/CSS/JS with no Node/Vite build step. Bundling
DeltaScribe would require adding a full JS build stage to that pipeline
(`npm install && npm run build` producing static assets to serve from
`/deltascribe/`), plus routing (e.g. a rewrite rule so `/deltascribe/` serves
its `index.html`) and submodule bookkeeping (`git submodule update --init`
on deploy, pinning/updating the submodule commit on each DeltaScribe release).

If DeltaScribe's API/URL params stabilize, bundling it same-origin is a
reasonable follow-up and would simplify this integration meaningfully: the
cross-origin CORS preflight handling and the HMAC-signed callback token in
`wordpresstv-deltascribe.php` both exist specifically to make the cross-origin
POST safe, and neither would be needed for a same-origin submodule build —
a plain nonce-verified admin-post handler would do. The `submitted_via` /
`_wptv_submitted_subtitles` postmeta contract, the bridge-page collection
flow, and the moderation pipeline this plugin feeds into would all stay
exactly as they are today either way.

To switch later: add DeltaScribe as a submodule (e.g. `git submodule add
<deltascribe-repo-url> wp-content/themes/wptv2/deltascribe`), add a build step
to `.github/workflows/dev.yaml`, add a rewrite so `/deltascribe/` resolves to
its built `index.html`, update `WordPressTV_DeltaScribe::get_url()` to point
at the local path by default, and simplify/remove the CORS + HMAC handling in
the `_submit` callback in favor of a standard WordPress nonce.
Loading
Loading