@GET
  @Path("download")
  public Response downloadVideo(@Context final MessageContext cxt, @Context final UriInfo uri) {
    // validate the session
    final Session session = this.getSession(cxt, uri);
    if (session == null || session.isExpired()) {
      return this.unauthorized(cxt, uri).build();
    }

    // generate URL for the requested video
    try {
      // find the output to be returned
      final AwsOutput output =
          this.txService.inReadOnlyTransaction(
              new Callable<AwsOutput>() {
                @Override
                public AwsOutput call() throws Exception {
                  // find the video
                  final Video video = getVideo(uri, session.getGuid());
                  if (video == null) {
                    return null;
                  }

                  // find the highest quality video available to download
                  return findOutput(video, getType(uri, Type.MP4));
                }
              });

      if (output != null && output.isDownloadable()) {
        final URL url = this.awsController.getSignedUrl(output.getFile());
        if (url != null) {
          return ResponseUtils.temporaryRedirect(url.toURI()).build();
        }
      }
    } catch (final Exception e) {
      LOG.debug("Error generating download url", e);
      return Response.status(Status.NOT_FOUND).build();
    }

    return Response.status(Status.NOT_FOUND).build();
  }
  @GET
  @Path("thumbnail")
  public Response getThumbnail(@Context final MessageContext cxt, @Context final UriInfo uri) {
    // validate the session
    final Session session = this.getSession(cxt, uri);
    if (session == null || session.isExpired()) {
      return this.unauthorized(cxt, uri).build();
    }

    // get the video
    final Video video = this.getVideo(uri, session.getGuid());
    if (video != null) {
      final URL thumbUrl = this.awsController.getSignedUrl(video.getThumbnail());
      if (thumbUrl != null) {
        try {
          return ResponseUtils.temporaryRedirect(thumbUrl.toURI()).build();
        } catch (final Exception ignored) {
        }
      }
    }

    return Response.status(Status.NOT_FOUND).build();
  }
  @GET
  @Path("stream")
  public Response getStream(@Context final MessageContext cxt, @Context final UriInfo uri) {
    // validate the session
    final Session session = this.getSession(cxt, uri);
    if (session == null || session.isExpired()) {
      return this.unauthorized(cxt, uri).build();
    }

    // ensure we have a video
    final Video video = this.getVideo(uri, session.getGuid());
    if (video == null) {
      return Response.status(Status.NOT_FOUND).build();
    }

    // switch based on type requested
    final Type type = this.getType(uri);
    switch (type) {
      case MP4:
      case MP4_720P:
      case MP4_480P_16_9:
        final AwsOutput output = findOutput(video, type);
        if (output != null) {
          try {
            final URL url = this.awsController.getSignedUrl(output.getFile());
            return ResponseUtils.temporaryRedirect(url.toURI())
                .header(HEADER_STREAM_URI, url)
                .build();
          } catch (final Exception ignored) {
          }
        }
        break;
      case HLS:
      case HLS_2M:
      case HLS_1M:
      case HLS_400K:
        try {
          final Playlist playlist;
          if (type == Type.HLS) {
            this.getRequestUriBuilder(cxt, uri);
            playlist = buildHlsMasterPlaylist(video, this.getRequestUriBuilder(cxt, uri));
          } else {
            playlist = buildHlsMediaPlaylist(video, type);
          }

          // return playlist if we have one
          if (playlist != null) {
            return Response.ok(playlist.toString())
                .type("application/vnd.apple.mpegurl")
                .header(HEADER_STREAM_URI, this.getRequestUri(cxt, uri))
                .build();
          }
        } catch (final Exception ignored) {
        }
        break;
      case UNKNOWN:
      default:
        return Response.status(Status.BAD_REQUEST).build();
    }

    return Response.status(Status.NOT_FOUND).build();
  }