Re-wrote all of the movie component. Prevented loading poster on mobile, loading placeholders for text, moved http calls to seasonedAPI util file and all functions have been re-implemented

This commit is contained in:
2019-06-02 22:41:12 +02:00
parent 33750f6a65
commit 682ca7bd1b

View File

@@ -1,269 +1,228 @@
<template> <template>
<section class="movie"> <section class="movie">
<div class="movie__container" v-if="movieLoaded">
<header class="movie__header" :class="{'movie__header--page': type=='page'}" :style="{ 'background-image': 'url(' + movieBackdropSrc + ')' }">
<div class="movie__wrap movie__wrap--header" :class="{'movie__wrap--page': type=='page'}">
<figure class="movie__poster">
<img v-if="moviePosterSrc" class="movie__img" src="~assets/placeholder.png" v-img="moviePosterSrc">
<img v-if="!moviePosterSrc" class="movies-item__img is-loaded" src="~assets/no-image.png">
</figure>
<div class="movie__title">
<h1 class="movie__title-text">
{{ movie.title }}
<!-- <span>{{ movie.type }}</span> -->
</h1>
<span>
</span>
</div>
</div>
</header>
<div class="movie__main">
<div class="movie__wrap movie__wrap--main" :class="{'movie__wrap--page': type=='page'}">
<!-- <div class="movie__ratings">
<p>here</p>
</div> -->
<!-- <div class="movie__actions" v-if="userLoggedIn && favoriteChecked"> -->
<div class="movie__actions">
<a class="movie__actions-link" v-if="matched" :class="{'active' : matched}"> <!-- HEADER w/ POSTER -->
<svg class="movie__actions-icon"> <header class="movie__header" :style="{ 'background-image': movie && backdrop !== null ? 'url(' + ASSET_URL + ASSET_SIZES[1] + backdrop + ')' : '' }">
<use xlink:href="#iconExsits"></use> <div class="movie__wrap movie__wrap--header">
</svg> <figure class="movie__poster">
<span class="movie__actions-text"> Already in plex &nbsp;🎉</span> <img v-if="movie && poster === null"
</a> class="movies-item__img is-loaded"
<a class="movie__actions-link" v-else="matched"> alt="movie poster image"
<svg class="movie__actions-icon"> src="~assets/no-image.png">
<use xlink:href="#iconNot_exsits"></use> <img v-else-if="poster === undefined"
</svg> class="movies-item__img grey"
<span class="movie__actions-text"> Not in plex yet</span> alt="movie poster image">
</a> <!-- src="~assets/placeholder.png"> -->
<img v-else
class="movies-item__img is-loaded"
alt="movie poster image"
:src="ASSET_URL + ASSET_SIZES[0] + poster">
</figure>
<a class="movie__actions-link" :class="{'active' : requested}" v-if="this.requested"> <div class="movie__title">
<svg class="movie__actions-icon"> <h1>{{ title }}</h1>
<use xlink:href="#iconSent"></use>
</svg>
<span class="movie__actions-text"> Requested to be downloaded</span>
</a>
<a class="movie__actions-link" v-else="this.requested" @click.prevent="sendRequest">
<svg class="movie__actions-icon" :class="{'waiting' : requested}">
<use xlink:href="#iconUnmatched"></use>
</svg>
<span class="movie__actions-text"> Request to be downloaded?</span>
</a>
<a class="movie__actions-link" @click="showTorrents=true" v-if="admin==='true'" :class="{'active' : showTorrents}">
<svg class="movie__actions-icon">
<use xlink:href="#icon_torrents"></use>
</svg>
<span class="movie__actions-text"> Search for torrents</span>
</a>
<a class="movie__actions-link" @click.prevent="openTmdb">
<svg class="movie__actions-icon">
<use xlink:href="#icon_info"></use>
</svg>
<span class="movie__actions-text"> See more info</span>
</a>
</div>
<div class="movie__info">
<div v-if="movie.summary" class="movie__description">
{{ movie.summary }}
</div>
<div class="movie__details">
<!-- <div v-if="movie.genres.length" class="movie__details-block">
<h2 class="movie__details-title">
Genres
</h2>
<div class="movie__details-text">
{{ nestedDataToString(movie.genres) }}
</div>
</div> -->
<div v-if="movie.year" class="movie__details-block">
<h2 class="movie__details-title">Release Date</h2>
<div class="movie__details-text">{{ movie.year }}</div>
</div>
<!-- <div v-if="movie.score" class="movie__details-block">
<h2 class="movie__details-title">Rating</h2>
<div class="movie__details-text">{{ rating }}</div>
</div> -->
<div v-if="movie.type == 'show'" class="movie__details-block">
<h2 class="movie__details-title">Seasons</h2>
<div class="movie__details-text">{{ movie.seasons }}</div>
</div>
</div>
</div>
<!-- <TableDemo class="movie__admin">This is it</TableDemo> -->
<div class="movie__admin" v-if="admin == 'true' && showTorrents">
<h2 class="movie__admin-title">torrents: {{ movie.title }}</h2>
<TorrentList :query="movie.title" :tmdb_id="movie.id"></TorrentList>
</div>
</div> </div>
</div> </div>
</header>
<!-- Siderbar and movie info -->
<div class="movie__main">
<div class="movie__wrap movie__wrap--main">
<!-- SIDEBAR ACTIONS -->
<div class="movie__actions" v-if="movie">
<a class="movie__actions-link" v-if="matched" :class="{'active' : matched}">
<svg class="movie__actions-icon"><use xlink:href="#iconExsits"></use></svg>
<span class="movie__actions-text"> Already in plex &nbsp;🎉</span>
</a>
<a class="movie__actions-link" v-else="matched">
<svg class="movie__actions-icon"><use xlink:href="#iconNot_exsits"></use></svg>
<span class="movie__actions-text"> Not in plex yet</span>
</a>
<a class="movie__actions-link" :class="{'active' : requested}" v-if="this.requested">
<svg class="movie__actions-icon"><use xlink:href="#iconSent"></use></svg>
<span class="movie__actions-text"> Requested to be downloaded</span>
</a>
<a class="movie__actions-link" v-else="this.requested" @click.prevent="sendRequest">
<svg class="movie__actions-icon" :class="{'waiting' : requested}"><use xlink:href="#iconUnmatched"></use></svg>
<span class="movie__actions-text"> Request to be downloaded?</span>
</a>
<a class="movie__actions-link" @click="showTorrents=!showTorrents" v-if="admin==='true'" :class="{'active' : showTorrents}">
<svg class="movie__actions-icon"><use xlink:href="#icon_torrents"></use></svg>
<span class="movie__actions-text"> Search for torrents</span>
</a>
<a class="movie__actions-link" @click.prevent="openTmdb">
<svg class="movie__actions-icon"><use xlink:href="#icon_info"></use></svg>
<span class="movie__actions-text"> See more info</span>
</a>
</div>
<!-- Loading placeholder -->
<div class="movie__actions text-input__loading" v-else>
<div class="movie__actions-link" v-for="_ in admin ? Array(4) : Array(3)">
<div class="movie__actions-text text-input__loading--line" style="margin:4.4px; margin-left: -3px;"></div>
</div>
</div>
<!-- MOVIE INFO -->
<div class="movie__info">
<div class="movie__description" v-if="movie"> {{ movie.overview }}</div>
<!-- Loading placeholder -->
<div v-else class="movie__description">
<loading-placeholder :count="12" />
</div>
<div class="movie__details" v-if="movie">
<div v-if="movie.year" class="movie__details-block">
<h2 class="movie__details-title">Release Date</h2>
<div class="movie__details-text">{{ movie.year }}</div>
</div>
<div v-if="movie.rank" class="movie__details-block">
<h2 class="movie__details-title">Rating</h2>
<div class="movie__details-text">{{ movie.rank }}</div>
</div>
<div v-if="movie.type == 'show'" class="movie__details-block">
<h2 class="movie__details-title">Seasons</h2>
<div class="movie__details-text">{{ movie.seasons }}</div>
</div>
<div v-if="movie.genres" class="movie__details-block">
<h2 class="movie__details-title">Genres</h2>
<div class="movie__details-text">{{ nestedDataToString(movie.genres) }}</div>
</div>
</div>
</div>
<!-- TODO: change this classname, this is general -->
<div class="movie__admin" v-if="movie && movie.credits">
<h2 class="movie__details-title">Cast</h2>
<div style="display: flex; flex-wrap: wrap;">
<person v-for="cast in movie.credits.cast" :info="cast"
style="flex-basis: 0;"></person>
</div>
</div>
</div>
<!-- TORRENT LIST -->
<TorrentList v-if="movie" :show="showTorrents" :query="title" :tmdb_id="id"
:admin="admin"></TorrentList>
</div> </div>
</section> </section>
</template> </template>
<script> <script>
import axios from 'axios'
import storage from '../storage.js' import storage from '../storage.js'
import img from '../directives/v-image.js' import img from '../directives/v-image.js'
import formatDate from '../directives/v-formatDate.js'
import TorrentList from './TorrentList.vue' import TorrentList from './TorrentList.vue'
import Person from './Person.vue'
import LoadingPlaceholder from './ui/LoadingPlaceholder.vue'
import { getMovie, getShow, request } from '../seasonedAPI.js'
export default { export default {
props: ['id', 'type', 'mediaType'], props: ['id', 'type'],
components: { TorrentList }, components: { TorrentList, Person, LoadingPlaceholder },
directives: { directives: { img: img }, // TODO decide to remove or use
img: img,
formatDate: formatDate
},
data(){ data(){
return{ return{
movie: {}, ASSET_URL: 'https://image.tmdb.org/t/p/',
movieLoaded: false, ASSET_SIZES: ['w500', 'w780', 'original'],
moviePosterSrc: '', movie: undefined,
movieBackdropSrc: '', title: undefined,
poster: undefined,
backdrop: undefined,
matched: false,
userLoggedIn: storage.sessionId ? true : false, userLoggedIn: storage.sessionId ? true : false,
favoriteChecked: false,
requested: false, requested: false,
admin: localStorage.getItem('admin'), admin: localStorage.getItem('admin'),
showTorrents: false showTorrents: false,
}
},
computed: {
loaded(){
return this.movieLoaded ? true : false;
} }
}, },
methods: { methods: {
fetchMovie(id){ parseResponse(resp) {
this.id = id; let movie = resp.data;
(this.mediaType == 'show') ? this.tmdbType = 'show' : this.tmdbType = 'movie' this.movie = { ...movie }
axios.get(`https://api.kevinmidboe.com/api/v1/plex/request/${id}?type=${this.mediaType}`) this.title = movie.title
.then(function(resp){ this.poster = movie.poster
let movie = resp.data; this.backdrop = movie.backdrop
this.movie = movie; this.matched = movie.existsInPlex;
this.poster(); // TODO should get a request response, used /plex/:id before
this.backdrop(); // this.requested = movie.requested;
this.matched = this.movie.matchedInPlex; this.requested = true
this.requested = this.movie.requested;
if(this.userLoggedIn){ document.title = movie.title + storage.pageTitlePostfix;
this.checkIfInFavorites(movie.id);
this.movieLoaded = true;
} else {
this.movieLoaded = true;
}
// Push state
if(storage.createMoviePopup){
storage.moviePath = this.mediaType + '/' + id;
history.pushState({ popup: true }, null, storage.moviePath);
storage.createMoviePopup = false;
}
// Change Page title
document.title = this.movie.title + storage.pageTitlePostfix;
}.bind(this))
.catch(function(error) {
console.log(error.response)
this.$router.push({ name: '404' });
}.bind(this));
},
poster() {
// Change the poster resolution
if(this.movie.poster_path){
this.moviePosterSrc = 'https://image.tmdb.org/t/p/w300' + this.movie.poster_path;
}
},
backdrop(){
if(this.movie.background_path){
this.movieBackdropSrc = 'https://image.tmdb.org/t/p/w500' + this.movie.background_path;
}
}, },
nestedDataToString(data) { nestedDataToString(data) {
let nestedArray = [], resultString; let nestedArray = []
data.forEach((item) => nestedArray.push(item.name)); data.forEach(item => nestedArray.push(item));
resultString = nestedArray.join(', '); return nestedArray.join(', ');
return resultString;
}, },
checkIfInFavorites(id){
// Change to check in plex
axios.get(`https://api.themoviedb.org/3/${this.tmdbType}/${id}/account_states?api_key=${storage.apiKey}&session_id=${storage.sessionId}`)
.then(function(resp){
this.favorite = resp.data.favorite;
this.favoriteChecked = true;
this.movieLoaded = true;
}.bind(this))
},
// Toggle the downloading status if admin
toggleFavorite(){
let favoriteInvert = !this.favorite;
this.favorite = '';
axios.post(`https://api.themoviedb.org/3/account/${storage.userId}/favorite?api_key=${storage.apiKey}&session_id=${storage.sessionId}`, {
'media_type': 'movie',
'media_id': this.id,
'favorite': favoriteInvert
})
.then(function(resp){
this.favorite = favoriteInvert;
eventHub.$emit('updateFavorite');
}.bind(this));
},
// Send a request for a specific movie
sendRequest(){ sendRequest(){
this.requested = '' request(this.id, this.type, storage.token)
axios({ .then(resp => {
method: 'post', //you can set what request you want to be if (resp.data.success)
url: `https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`, this.requested = true
headers: { })
authorization: storage.token
}
})
// axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`, {}, {
// authorization: storage.token
// })
// axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?api_key=${storage.apiKey}&session_id=${storage.sessionId}`, {
.then(function(resp){
if (resp.data.success)
this.requested = true;
else
this.requested = false;
}.bind(this));
}, },
openTmdb(){ openTmdb(){
window.location.replace('https://www.themoviedb.org/' + this.tmdbType + '/' + this.id) const tmdbType = this.type === 'show' ? 'tv' : this.type
window.location.href = 'https://www.themoviedb.org/' + tmdbType + '/' + this.id
}, },
// Search torrents by query
// searchForTorrents() {
// axios.get(`https://apollo.kevinmidboe.com/api/v1/plex/request/${id}?type=${'movie'}&api_key=${storage.apiKey}&language=en-US`)
// },
}, },
watch: { watch: {
id: function(val){ id: function(val){
this.fetchMovie(val); if (this.type === 'movie') {
this.fetchMovie(val);
} else {
this.fetchShow(val)
}
} }
}, },
beforeDestroy() {
document.title = this.prevDocumentTitle
},
created(){ created(){
this.fetchMovie(this.id); this.prevDocumentTitle = document.title
if (this.type === 'movie') {
getMovie(this.id)
.then(this.parseResponse)
.catch(error => {
this.$router.push({ name: '404' });
})
} else {
getShow(this.id)
.then(this.parseResponse)
.catch(error => {
this.$router.push({ name: '404' });
})
}
console.log('admin: ', this.admin) console.log('admin: ', this.admin)
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
@import "./src/scss/loading-placeholder";
@import "./src/scss/variables"; @import "./src/scss/variables";
@import "./src/scss/media-queries"; @import "./src/scss/media-queries";
.movie{ .movie{
&__wrap{ &__wrap{
display: flex; display: flex;
&--page{
max-width: 768px;
position: relative;
margin: 0 auto;
}
&--header{ &--header{
align-items: center; align-items: center;
height: 100%; height: 100%;
@@ -284,13 +243,10 @@ export default {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 50% 50%; background-position: 50% 50%;
background-color: $c-dark; background-color: $c-dark;
@include tablet-min{ @include tablet-min {
height: 350px; height: 350px;
&--page{
height: 384px;
}
} }
&:before{ &:before {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
@@ -302,65 +258,74 @@ export default {
background: rgba($c-dark, 0.85); background: rgba($c-dark, 0.85);
} }
} }
&__poster{
display: none; &__poster{
@include tablet-min{ display: none;
background: $c-dark; @include tablet-min{
display: block; background: $c-white;
position: absolute; height: 0;
width: calc(45% - 40px); display: block;
top: 40px; position: absolute;
left: 40px; width: calc(45% - 40px);
top: 40px;
left: 40px;
}
}
&__img {
display: block;
width: 100%;
opacity: 0;
transform: scale(0.97) translateZ(0);
transition: opacity 0.5s ease, transform 0.5s ease;
&.is-loaded{
opacity: 1;
transform: scale(1);
}
}
&__title {
position: relative;
padding: 20px;
color: $c-green;
text-align: center;
width: 100%;
@include tablet-min {
width: 55%;
text-align: left;
margin-left: 45%;
padding: 30px 30px 30px 40px;
}
h1 {
font-weight: 500;
line-height: 1.4;
font-size: 24px;
@include tablet-min {
font-size: 30px;
} }
} }
&__img{ span {
display: block; display: block;
width: 100%; font-size: 14px;
opacity: 0; font-weight: 300;
transform: scale(0.97) translateZ(0); color: rgba($c-white, 0.7);
transition: opacity 0.5s ease, transform 0.5s ease; margin-top: 10px;
&.is-loaded{
opacity: 1;
transform: scale(1);
}
}
&__title{
position: relative;
padding: 20px;
color: $c-green;
text-align: center;
width: 100%;
@include tablet-min{
width: 55%;
text-align: left;
margin-left: 45%;
padding: 30px 30px 30px 40px;
}
&-text{
font-weight: 500;
line-height: 1.4;
font-size: 24px;
@include tablet-min{
font-size: 30px;
}
span{
display: block;
font-size: 14px;
font-weight: 300;
color: rgba($c-white, 0.7);
margin-top: 10px;
}
}
} }
}
&__main{ &__main{
background: $c-light; background: $c-light;
min-height: calc(100vh - 250px); min-height: calc(100vh - 250px);
@include tablet-min{ @include tablet-min {
min-height: 0; min-height: 0;
} }
height: 100%;
} }
&__actions{ &__actions{
text-align: center; text-align: center;
// min-height: 394px;
width: 100%; width: 100%;
order: 2; order: 2;
padding: 20px; padding: 20px;