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

Add video info view #1014

Merged
merged 15 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 6 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
Expand Up @@ -18,7 +18,7 @@ public function show($id)
$annotation = VideoAnnotation::findOrFail($id);
$this->authorize('access', $annotation);

return redirect()->route('video', [
return redirect()->route('video-annotate', [
'id' => $annotation->video_id,
'annotation' => $annotation->id,
]);
Expand Down
36 changes: 36 additions & 0 deletions app/Http/Controllers/Views/Volumes/VideoController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Biigle\Http\Controllers\Views\Volumes;

use Arr;
use Biigle\Http\Controllers\Views\Controller;
use Biigle\Video;

class VideoController extends Controller
{
/**
* Shows the video index page.
*
* @param int $id volume ID
*/
public function index($id)
{
$video = Video::with('volume')->findOrFail($id);

$this->authorize('access', $video);

$metadataMap = [
'gps_altitude' => 'GPS Altitude',
'distance_to_ground' => 'Distance to ground',
'yaw' => 'Yaw/Heading',
'area' => 'Area',
];

return view('volumes.videos.index', [
'video' => $video,
'volume' => $video->volume,
'metadata' => Arr::only($video->metadata, array_keys($metadataMap)),
'metadataMap' => $metadataMap,
]);
}
}
76 changes: 76 additions & 0 deletions resources/assets/js/volumes/components/metadataModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<modal
id="modal-show-metadata"
mzur marked this conversation as resolved.
Show resolved Hide resolved
ref="modal"
v-model="show"
size="lg"
:title="name"
:footer="false"
>
<div class="panel panel-default table-responsive">
<table class="table">
<thead>
<tr>
<th v-for="time in times">{{ time }}</th>
</tr>
</thead>
<tbody>
<tr v-if="items">
<td v-for="item in items">{{ item }}</td>
</tr>
</tbody>
</table>
</div>
</modal>
</template>

<script>
import { Modal } from 'uiv';

export default {
components: {
modal: Modal,
},
props: {
showModal: {
required: true,
type: Boolean,
},
times: {
required: true,
type: Array,
},
items: {
required: true,
type: Array,
},
name: {
required: true,
type: String,
},
},
data() {
return {
show: false,
};
},
watch: {
// if volume-metadata-button pressed, trigger modal
showModal: function () {
if (this.showModal) {
this.show = true;
}
},
// if modal is closed, trigger the close-modal-event, which sets 'showModal' in parent container to false again
show: function() {
if (this.show === false) {
this.$emit('close-modal');
}
}
},
created() {
this.show = false;
this.$emit('load-modal');
}
}
</script>
2 changes: 2 additions & 0 deletions resources/assets/js/volumes/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import MetadataUpload from './metadataUpload';
import ProjectsBreadcrumb from './projectsBreadcrumb';
import SearchResults from './searchResults';
import VolumeContainer from './volumeContainer';
import VolumeMetadata from './volumeMetadata.vue';

biigle.$mount('annotation-session-panel', AnnotationSessionPanel);
biigle.$mount('create-volume-form-step-1', CreateFormStep1);
Expand All @@ -30,3 +31,4 @@ biigle.$mount('search-results', SearchResults);
biigle.$mount('volume-container', VolumeContainer);
biigle.$mount('volume-file-count', FileCount);
biigle.$mount('volume-metadata-upload', MetadataUpload);
biigle.$mount('volume-metadata-modal', VolumeMetadata);
57 changes: 57 additions & 0 deletions resources/assets/js/volumes/volumeMetadata.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script>
import LoaderMixin from '../core/mixins/loader';
import Messages from '../core/messages/store';
import metadataModal from './components/metadataModal';

/**
* The video container for metadata.
*/
export default {
mixins: [
LoaderMixin,
],
components: {
metadataModal: metadataModal,
},
data() {
return {
showModal: false,
times: [],
items: [],
name: "",
};
},
methods: {
getTimes(values) {
// If modal has been opened before, use cached data
if (this.times.length === 0) {
let dateStrings = [];
const options = { year: "numeric", month: "numeric", day: "numeric", hour: '2-digit', minute:'2-digit', second:'2-digit'}
values.forEach(element => {
dateStrings.push(new Date(element).toLocaleDateString(undefined, options));
});
this.times = dateStrings;
}
},
showTimes() {
if (this.times.length === 0)
this.handleError("No times data found.");
this.name = "Times";
this.showModal = true;
},
getMetadata(key, data) {
this.name = key;
this.items = data;
this.showModal = true;
},
hideMetadataModal() {
// reset items for next metadata
this.items = [];
this.showModal = false;
},
handleError(message) {
Messages.danger(message);
},
},
};
</script>
2 changes: 1 addition & 1 deletion resources/views/search/videos-content.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ul id="search-results" class="row volume-search-results">
@foreach ($results as $video)
<li class="col-xs-4">
<a href="{{route('video', $video->id)}}" title="Show video {{$video->filename}}">
<a href="{{route('video-annotate', $video->id)}}" title="Show video {{$video->filename}}">
<preview-thumbnail class="preview-thumbnail" :id="{{$video->id}}" thumb-uris="{{$video->thumbnailsUrl->implode(',')}}">
<img src="{{ $video->thumbnailUrl }}" onerror="this.src='{{ asset(config('thumbnails.empty_url')) }}'">
<figcaption slot="caption">{{$video->filename}}</figcaption>
Expand Down
2 changes: 1 addition & 1 deletion resources/views/videos/dashboardActivityItem.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<a class="activity-item" href="{{route('video', $item->id)}}" title="Show video {{$item->name}}">
<a class="activity-item" href="{{route('video-annotate', $item->id)}}" title="Show video {{$item->name}}">
<figure class="activity-item-image">
<i class="icon fas fa-film fa-lg"></i>
<img src="{{ $item->thumbnailUrl }}" onerror="this.src='{{ asset(config('thumbnails.empty_url')) }}'">
Expand Down
4 changes: 2 additions & 2 deletions resources/views/volumes/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
biigle.$declare('volumes.infoUri', '{{ route('image', ':id') }}');
@else
biigle.$declare('volumes.thumbCount', {{ config('videos.thumbnail_count') }});
biigle.$declare('volumes.annotateUri', '{{ route('video', ':id') }}');
biigle.$declare('volumes.infoUri', undefined);
biigle.$declare('volumes.annotateUri', '{{ route('video-annotate', ':id') }}');
biigle.$declare('volumes.infoUri', '{{ route('video', ':id') }}');
@endif

biigle.$declare('volumes.userId', {!! $user->id !!});
Expand Down
33 changes: 33 additions & 0 deletions resources/views/volumes/videos/index.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@extends('app')
@section('title', $video->filename)

@section('content')
<div class="container">
<div class="row">
<h2 class="col-lg-12 clearfix file-info-title" title="{{ $video->filename }}">
{{ $video->filename }}
<span class="pull-right">
<a href="{{route('volume', $volume->id)}}" title="Back to {{ $volume->name }}" class="btn btn-default">back</a>
@mixin('imagesIndexButtons')
</span>
</h2>
</div>
<div class="row">
<div class="col-sm-6 col-lg-4">
<div class="panel panel-default panel-image">
<img src="{{ $video->thumbnail_url }}" onerror="this.src='{{ asset(config('thumbnails.empty_url')) }}'">
</div>
@include('volumes.videos.index.meta')
</div>
</div>
</div>
@endsection

@section('navbar')
<div class="navbar-text navbar-volumes-breadcrumbs">
@include('volumes.partials.projectsBreadcrumb', ['projects' => $volume->projects]) /
<a href="{{route('volume', $volume->id)}}" class="navbar-link" title="Show volume {{$volume->name}}">{{$volume->name}}</a> /
<strong title="{{$video->filename}}">{{$video->filename}}</strong>
@include('volumes.partials.annotationSessionIndicator')
</div>
@endsection
59 changes: 59 additions & 0 deletions resources/views/volumes/videos/index/meta.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<div id="volume-metadata-modal" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Video information</h3>
</div>
<table class="table">
@if ($video->width && $video->height)
<tr>
<th>Dimensions</th>
<td>{{ $video->width }} &times; {{ $video->height }} px </td>
</tr>
@endif
@if ($video->size)
<tr>
<th>Size</th>
<td>{{ round($video->size / 1E+6, 2) }} MBytes </td>
</tr>
@endif
@if ($video->mimetype)
<tr>
<th>MIME</th>
<td><code>{{ $video->mimetype }}</code></td>
</tr>
@endif
@if ($video->taken_at)
@if (is_array($video->taken_at))
<metadata-modal v-bind:show-modal="showModal" v-bind:times="times" v-bind:items="items" v-bind:name="name" v-on:load-modal="getTimes({{ collect($video->taken_at) }})" v-on:close-modal="hideMetadataModal"></metadata-modal>
<tr>
<th>Created</th>
<td>
<button class="btn btn-default" type="button" title="Show full timestamps" v-on:click.prevent="showTimes()">Show values</button>
</td>
</tr>
@foreach ($metadata as $field => $value)
<tr>
<th>{{ $metadataMap[$field] }}</th>
@if (is_array($value))
<td>
<button class="btn btn-default" type="button" title="Show full metadata array" v-on:click.prevent="getMetadata({{ json_encode($metadataMap[$field]) }}, {{ collect($value) }})">Show values</button>
mzur marked this conversation as resolved.
Show resolved Hide resolved
</td>
@else
<td>{{ $value }}</td>
@endif
</tr>
@endforeach
@else
<tr>
<th>Created</th>
<td>{{ $video->taken_at }}</td>
</tr>
@foreach ($metadata as $field => $value)
<tr>
<th>{{ $metadataMap[$field] }}</th>
<td>{{ $value }}</td>
</tr>
@endforeach
@endif
@endif
</table>
</div>
9 changes: 7 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,14 @@
$router->redirect('annotations/{id}', '/image-annotations/{id}');
});

$router->get('videos/{id}', [
'as' => 'video',
'uses' => 'Volumes\VideoController@index',
]);

$router->group(['namespace' => 'Videos'], function ($router) {
$router->get('videos/{id}/annotations', [
'as' => 'video',
'as' => 'video-annotate',
'uses' => 'VideoController@show',
]);

Expand All @@ -355,7 +360,7 @@
]);

// Legacy support.
$router->redirect('videos/{id}', '/videos/{id}/annotations');
$router->redirect('video-annotate/{id}', '/videos/{id}/annotations');
mzur marked this conversation as resolved.
Show resolved Hide resolved
});

});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,4 @@ public function testShow()
$this->beGuest();
$this->get("videos/{$video->id}/annotations")->assertStatus(200);
}

public function testShowRedirect()
{
$this->beUser();
$this->get('videos/999')->assertRedirect('/videos/999/annotations');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Biigle\Tests\Http\Controllers\Views\Volumes;

use Biigle\Tests\ProjectTest;
use Biigle\Tests\UserTest;
use Biigle\Tests\VideoTest;
use TestCase;

class VideoInfoControllerTest extends TestCase
{
public function testIndex()
{
$project = ProjectTest::create();
$user = UserTest::create();
$video = VideoTest::create();
$project->addVolumeId($video->volume->id);

// not logged in
$response = $this->get('videos/'.$video->id);
$response->assertStatus(302);

// doesn't belong to project
$this->be($user);
$response = $this->get('videos/'.$video->id);
$response->assertStatus(403);

$this->be($project->creator);
$response = $this->get('videos/'.$video->id);
$response->assertStatus(200);

// doesn't exist
$response = $this->get('videos/-1');
$response->assertStatus(404);
}
}
Loading