Added filtering of requested movies. Also change the order of movies in sidebar and added support for rating of movie. Change path destinations to be more clear.

This commit is contained in:
2018-07-25 17:53:01 +02:00
parent aa9bfa12d9
commit 8c38f82dbe
13 changed files with 245 additions and 74 deletions

View File

@@ -109,6 +109,20 @@
<div id="app"></div> <div id="app"></div>
<script src="dist/build.js"></script> <script src="dist/build.js"></script>
</body> </body>
<script>
(function(w, d){
var b = d.getElementsByTagName('body')[0];
var s = d.createElement("script"); s.async = true;
var v = !("IntersectionObserver" in w) ? "8.7.1" : "10.5.2";
s.src = "https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/" + v + "/lazyload.min.js";
w.lazyLoadOptions = {
container: '.movies__list',
elements_selector: 'img',
threshold: 300,
}; // Your options here. See "recipes" for more information about async.
b.appendChild(s);
}(window, document));
</script>
<script src="https://cdn.jsdelivr.net/vue/2.0.0-rc/vue.min.js"></script> <script src="https://cdn.jsdelivr.net/vue/2.0.0-rc/vue.min.js"></script>
<script src="https://cdn.ravenjs.com/3.23.1/vue/raven.min.js" crossorigin="anonymous"></script> <script src="https://cdn.ravenjs.com/3.23.1/vue/raven.min.js" crossorigin="anonymous"></script>
<script>Raven.config('https://c1fa1a17de3d4b24abcd05161648fe4d@sentry.io/300063').install();</script> <script>Raven.config('https://c1fa1a17de3d4b24abcd05161648fe4d@sentry.io/300063').install();</script>

View File

@@ -5,19 +5,17 @@ var history = require('connect-history-api-fallback');
app = express(); app = express();
app.use('/dist', express.static(path.join(__dirname + "/dist"))); app.use('/dist', express.static(path.join(__dirname + "/dist")));
app.use('/dist', express.static(path.join(__dirname + "/dist/")));
app.use('/favicons', express.static(path.join(__dirname + "/favicons"))); app.use('/favicons', express.static(path.join(__dirname + "/favicons")));
app.use(history({ app.use(history({
index: '/' index: '/'
})); }));
var port = process.env.PORT || 5000;
app.get('/', function(req, res) { app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/index.html')); res.sendFile(path.join(__dirname + '/index.html'));
}); });
app.listen(port); app.listen(port);
console.log('server started '+ port);

View File

@@ -3,7 +3,7 @@
<navigation></navigation> <navigation></navigation>
<header class="header"> <header class="header">
<div class="header__search"> <div class="header__search">
<input class="header__search-input" type="text" v-model.trim="searchQuery" @keyup.enter="search" @blur="search" placeholder="Search for a movie or show..."> <input class="header__search-input" type="text" v-model.trim="searchQuery" @keyup.enter="search" placeholder="Search for a movie or show...">
<svg class="header__search-icon"> <svg class="header__search-icon">
<use xlink:href="#iconSearch"></use> <use xlink:href="#iconSearch"></use>
</svg> </svg>
@@ -301,6 +301,11 @@ img{
background: $c-dark; background: $c-dark;
color: $c-white; color: $c-white;
} }
&__active {
@extend .button;
background: $c-dark;
color: $c-white;
}
} }

View File

@@ -19,6 +19,9 @@
</header> </header>
<div class="movie__main"> <div class="movie__main">
<div class="movie__wrap movie__wrap--main" :class="{'movie__wrap--page': type=='page'}"> <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" v-if="userLoggedIn && favoriteChecked"> -->
<div class="movie__actions"> <div class="movie__actions">
@@ -76,20 +79,18 @@
</div> </div>
</div> --> </div> -->
<div v-if="movie.year" class="movie__details-block"> <div v-if="movie.year" class="movie__details-block">
<h2 class="movie__details-title"> <h2 class="movie__details-title">Release Date</h2>
Release Date <div class="movie__details-text">{{ movie.year }}</div>
</h2>
<div class="movie__details-text">
{{ movie.year }}
</div>
</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"> <div v-if="movie.type == 'show'" class="movie__details-block">
<h2 class="movie__details-title"> <h2 class="movie__details-title">Seasons</h2>
Seasons <div class="movie__details-text">{{ movie.seasons }}</div>
</h2>
<div class="movie__details-text">
10
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -130,7 +131,9 @@ export default {
favoriteChecked: false, favoriteChecked: false,
requested: false, requested: false,
admin: localStorage.getItem('admin'), admin: localStorage.getItem('admin'),
showTorrents: false showTorrents: false,
rating_int: 0,
rating_dec: 0
} }
}, },
computed: { computed: {
@@ -161,7 +164,7 @@ export default {
} }
// Push state // Push state
if(storage.createMoviePopup){ if(storage.createMoviePopup){
storage.moviePath = '/request/' + this.mediaType + '/' + id; storage.moviePath = this.mediaType + '/' + id;
history.pushState({ popup: true }, null, storage.moviePath); history.pushState({ popup: true }, null, storage.moviePath);
storage.createMoviePopup = false; storage.createMoviePopup = false;
} }
@@ -219,6 +222,13 @@ export default {
axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`, axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`,
{ headers: {authorization: storage.token} }, { headers: {authorization: storage.token} },
) )
axios({
method: 'post', //you can set what request you want to be
url: `https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`,
headers: {
authorization: storage.token
}
})
// axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?api_key=${storage.apiKey}&session_id=${storage.sessionId}`, { // axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?api_key=${storage.apiKey}&session_id=${storage.sessionId}`, {
.then(function(resp){ .then(function(resp){
if (resp.data.success) if (resp.data.success)

View File

@@ -40,20 +40,30 @@ let removed;
export default { export default {
props: ['type', 'mode', 'category', 'shortList'], props: ['type', 'mode', 'category', 'shortList'],
components: { MoviesListItem }, components: { MoviesListItem },
beforeRouteLeave (to, from, next) { // beforeRouteLeave (to, from, next) {
if(from.name == 'search'){ // if(from.name == 'search'){
eventHub.$emit('setSearchQuery', true); // eventHub.$emit('setSearchQuery', true);
} // }
next(); // next();
}, // },
data() { data() {
return { return {
listTitle: '', listTitle: '',
movies: [], movies: [],
unfiltered_movies: [],
pages: '', pages: '',
filter: '',
filter_query: '',
results: '', results: '',
currentPage: 1, currentPage: 1,
listLoaded: false listLoaded: false,
showFilter: false,
filters: {
status: {
elms: ['all', 'requested', 'downloading', 'downloaded'],
selected: 0,
}
}
} }
}, },
computed: { computed: {
@@ -68,7 +78,7 @@ export default {
if(this.mode == 'search'){ if(this.mode == 'search'){
return `https://api.kevinmidboe.com/api/v1/plex/request?query=${this.query}&page=${this.currentPage}`; return `https://api.kevinmidboe.com/api/v1/plex/request?query=${this.query}&page=${this.currentPage}`;
} else if(this.mode == 'requests' || this.$route.params.category == 'requests') { } else if(this.mode == 'requests' || this.$route.params.category == 'requests') {
return `https://api.kevinmidboe.com/api/v1/plex/requests/all?page=${this.currentPage}&status=requested`; return `https://api.kevinmidboe.com/api/v1/plex/requests/all?page=${this.currentPage}&status=${this.filter}`;
} else if(this.mode == 'collection') { } else if(this.mode == 'collection') {
let category = this.$route.params.category || this.category; let category = this.$route.params.category || this.category;
return `https://api.kevinmidboe.com/api/v1/tmdb/list/${category}?page=${this.currentPage}`; return `https://api.kevinmidboe.com/api/v1/tmdb/list/${category}?page=${this.currentPage}`;
@@ -104,6 +114,7 @@ export default {
this.pages = data.total_pages; this.pages = data.total_pages;
this.results = data.total_results; this.results = data.total_results;
} }
this.unfiltered_movies = this.movies;
this.listLoaded = true; this.listLoaded = true;
// Change Page title // Change Page title
if(this.type == 'page'){ if(this.type == 'page'){
@@ -123,32 +134,51 @@ export default {
this.movies = newData; this.movies = newData;
}.bind(this)); }.bind(this));
}, },
updateFavorite(){ sortt() {
if(this.mode == 'favorite'){ console.log(this.showFilters)
let promises = [], movies = [], pages, results; },
for(let i = 1; i <= this.currentPage; i++){ toggleFilter(item, index){
promises.push(axios.get(`https://api.themoviedb.org/3/account/${storage.userId}/favorite/movies?api_key=${storage.apiKey}&session_id=${storage.sessionId}&language=en-US&sort_by=created_at.desc&page=${i}`)) this.showFilter = this.showFilter ? false : true;
} // this.results = this.results.filter(result => result.status != 'downloaded')
axios.all(promises).then(function(results) { },
results.forEach(function(resp) { applyFilter(item, index) {
let data = resp.data; this.filter = item;
movies = movies.concat(data.results); this.filters.status.selected = index;
pages = data.total_pages; console.log('applied query filter: ', item, index)
results = data.total_results; this.fetchCategory()
});
this.movies = movies;
this.pages = pages;
if(this.currentPage > pages){
this.currentPage -= 1;
}
this.results = results;
}.bind(this));
}
} }
// updateFavorite(){
// if(this.mode == 'favorite'){
// let promises = [], movies = [], pages, results;
// for(let i = 1; i <= this.currentPage; i++){
// promises.push(axios.get(`https://api.themoviedb.org/3/account/${storage.userId}/favorite/movies?api_key=${storage.apiKey}&session_id=${storage.sessionId}&language=en-US&sort_by=created_at.desc&page=${i}`))
// }
// axios.all(promises).then(function(results) {
// results.forEach(function(resp) {
// let data = resp.data;
// movies = movies.concat(data.results);
// pages = data.total_pages;
// results = data.total_results;
// });
// this.movies = movies;
// this.pages = pages;
// if(this.currentPage > pages){
// this.currentPage -= 1;
// }
// this.results = results;
// }.bind(this));
// }
// }
}, },
watch: { watch: {
query(value){ filter_query: function(val, oldVal) {
this.fetchCategory(value); let movies = this.unfiltered_movies;
val = val.toLowerCase()
if (val.length > 0)
movies = movies.filter(movie => movie.title.toLowerCase().startsWith(val))
if (movies.length > 0)
this.movies = movies;
} }
}, },
created(){ created(){
@@ -171,7 +201,21 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
@import "./src/scss/media-queries";
.form__group-input {
padding: 10px 5px 10px 15px;
margin-left: 0;
height: 38px;
width: 150px;
font-size: 15px;
@include desktop-min {
width: 200px;
font-size: 17px;
}
}
</style>
<style lang="scss"> <style lang="scss">
@import "./src/scss/variables"; @import "./src/scss/variables";
@import "./src/scss/media-queries"; @import "./src/scss/media-queries";
@@ -188,6 +232,7 @@ export default {
} }
&__header{ &__header{
display: flex; display: flex;
flex-flow: row wrap;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 20px 10px; padding: 20px 10px;
@@ -207,6 +252,8 @@ export default {
line-height: 16px; line-height: 16px;
color: $c-dark; color: $c-dark;
font-weight: 300; font-weight: 300;
flex-basis: 50%;
@include tablet-min{ @include tablet-min{
font-size: 18px; font-size: 18px;
line-height: 18px; line-height: 18px;
@@ -217,6 +264,12 @@ export default {
font-weight: 300; font-weight: 300;
letter-spacing: 0.5px; letter-spacing: 0.5px;
color: rgba($c-dark, 0.5); color: rgba($c-dark, 0.5);
text-align: right;
flex-basis: 50%;
@include mobile-only {
display: none;
}
} }
&__link{ &__link{
font-size: 12px; font-size: 12px;
@@ -232,6 +285,71 @@ export default {
color: $c-dark; color: $c-dark;
} }
} }
&__filters{
margin-top: 10px;
line-height: 22px;
color: $c-dark;
font-size: 18px;
display: flex;
justify-content: flex-end;
transition: opacity 1s ease;
&__button-spacing {
@include tablet-min {
width: 15px;
}
@include mobile-only {
width: 10px;
}
}
&-list {
margin: 0px 10px;
padding: 0;
list-style: none;
border: solid 1px;
border-radius: 2px;
overflow: hidden;
display: flex;
transition: color 0.2s ease;
justify-content: space-evenly;
@include tablet-min{
margin: 0px 15px;
}
@include tablet-landscape-min {
margin: 0px 25px;
}
@include desktop-min{
margin: 0px 30px;
}
li {
padding: 6px 14px;
background-color: $c-white;
transition: color 0.2s ease;
font-size: 13px;
font-weight: 200;
text-transform: capitalize;
text-align: center;
width: 100%;
&:nth-child(n+2) {
border-left: solid 1px;
}
&.active, &:hover {
border-color: transparent;
background-color: #091c24;
color: $c-white;
cursor: pointer;
}
@include tablet-min {
font-size: 16px;
}
}
}
&-toggle {
margin-left: 15px;
}
}
&__list{ &__list{
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@@ -3,9 +3,18 @@
<div class="profile__content" v-if="userLoggedIn"> <div class="profile__content" v-if="userLoggedIn">
<header class="profile__header"> <header class="profile__header">
<h2 class="profile__title">{{ emoji }} Welcome {{ userName }}</h2> <h2 class="profile__title">{{ emoji }} Welcome {{ userName }}</h2>
<div>
<router-link class="" :to="{name: 'settings'}">
</router-link>
<button v-if="showSettings" class="button__active" @click="toggleSettings" style="margin-right: 2em;">Hide settings</button>
<button v-else class="button" @click="toggleSettings" style="margin-right: 2em;">Show settings</button>
<button class="button" @click="logOut">Log Out</button> <button class="button" @click="logOut">Log Out</button>
</div>
</header> </header>
<movies-list v-for="item in listTypes" v-if="item.isProfileContent" :type="'component'" :mode="item.type" :category="item.query" :shortList="true"></movies-list> <settings v-if="showSettings"></settings>
<movies-list v-for="item in listTypes" v-if="!showSettings && item.isProfileContent" :type="'component'" :mode="item.type" :category="item.query" :shortList="true"></movies-list>
<!-- <movies-list v-for="item in listTypes" v-if="item.isCategory" :type="'component'" :mode="item.type" :shortList="true"></movies-list> --> <!-- <movies-list v-for="item in listTypes" v-if="item.isCategory" :type="'component'" :mode="item.type" :shortList="true"></movies-list> -->
<!-- <created-lists></created-lists> --> <!-- <created-lists></created-lists> -->
</div> </div>
@@ -24,15 +33,17 @@
import axios from 'axios' import axios from 'axios'
import storage from '../storage.js' import storage from '../storage.js'
import MoviesList from './MoviesList.vue' import MoviesList from './MoviesList.vue'
import Settings from './Settings.vue'
// import CreatedLists from './CreatedLists.vue' // import CreatedLists from './CreatedLists.vue'
export default { export default {
components: { MoviesList }, components: { MoviesList, Settings },
data(){ data(){
return{ return{
userLoggedIn: '', userLoggedIn: '',
userName: '', userName: '',
emoji: '', emoji: '',
showSettings: false,
listTypes: storage.listTypes listTypes: storage.listTypes
} }
}, },
@@ -62,6 +73,9 @@ export default {
requestToken(){ requestToken(){
eventHub.$emit('requestToken'); eventHub.$emit('requestToken');
}, },
toggleSettings() {
this.showSettings = this.showSettings ? false : true;
},
logOut(){ logOut(){
localStorage.clear(); localStorage.clear();
eventHub.$emit('setUserStatus'); eventHub.$emit('setUserStatus');

View File

@@ -8,7 +8,7 @@
<form class="form"> <form class="form">
<div class="form__buffer"></div> <div class="form__buffer"></div>
<div> <div class="center">
<div class="form__group"> <div class="form__group">
<svg class="form__group__input-icon"> <svg class="form__group__input-icon">
<use xlink:href="#iconEmail"></use> <use xlink:href="#iconEmail"></use>
@@ -203,6 +203,9 @@ export default {
color: #3e7549; color: #3e7549;
} }
.center {
justify-content: center;
}
.form{ .form{
z-index: 15; z-index: 15;
background-color: $c-light; background-color: $c-light;
@@ -229,7 +232,6 @@ export default {
font-weight: 100; font-weight: 100;
font-size: 20px; font-size: 20px;
border: 1px solid $c-dark; border: 1px solid $c-dark;
margin-bottom: 1rem;
margin-left: -2.2rem; margin-left: -2.2rem;
z-index: 3; z-index: 3;
&:focus, &:hover { &:focus, &:hover {

View File

@@ -18,7 +18,7 @@
<svg class="form__group__input-icon"> <svg class="form__group__input-icon">
<use xlink:href="#iconKeyhole"></use> <use xlink:href="#iconKeyhole"></use>
</svg> </svg>
<input class="form__group-input" type="password" ref="password" placeholder="Password"> <input class="form__group-input" type="password" ref="password" placeholder="Password" v-on:keyup.enter="signin">
</div> </div>
<transition name="message-fade"> <transition name="message-fade">

View File

@@ -63,7 +63,7 @@ export default {
renderName: tablet, renderName: tablet,
cols: [ cols: [
{ label: 'Name', field: 'name', sort: true, hidden: tablet }, { label: 'Name', field: 'name', sort: true, hidden: tablet },
{ label: 'Seeders', field: 'seed', sort: true }, { label: 'Seeders', field: 'seed', sort: (a, b) => parseInt(a) - parseInt(b) },
{ label: 'Size', field: 'size', sort: (a, b) => this.sortableSize(a) - this.sortableSize(b) }, { label: 'Size', field: 'size', sort: (a, b) => this.sortableSize(a) - this.sortableSize(b) },
{ label: 'Add', align: 'center' } { label: 'Add', align: 'center' }
], ],

View File

@@ -2,7 +2,9 @@ import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import axios from 'axios' import axios from 'axios'
import router from './routes' import router from './routes'
import DataTablee from 'vue-data-tablee' import DataTablee from 'vue-data-tablee'
import VModal from 'vue-js-modal'
import App from './App.vue' import App from './App.vue'
@@ -10,6 +12,7 @@ window.eventHub = new Vue();
Vue.use(VueRouter, axios) Vue.use(VueRouter, axios)
Vue.use(DataTablee) Vue.use(DataTablee)
Vue.use(VModal, { dialog: true })
new Vue({ new Vue({
el: '#app', el: '#app',

View File

@@ -85,6 +85,13 @@ let routes = [
'search-router-view': require('./components/Profile.vue') 'search-router-view': require('./components/Profile.vue')
} }
}, },
{
name: 'settings',
path: '/profile/settings',
components: {
'search-router-view': require('./components/Settings.vue')
}
},
{ {
name: '404', name: '404',
path: '/404', path: '/404',
@@ -99,8 +106,8 @@ let routes = [
]; ];
const router = new VueRouter({ const router = new VueRouter({
mode: 'history', // mode: 'history',
base: '/request', base: '/request/',
routes, routes,
linkActiveClass: 'is-active' linkActiveClass: 'is-active'
}); });

View File

@@ -6,12 +6,13 @@ let storage = {
pageTitlePostfix: ' — ' + document.title, pageTitlePostfix: ' — ' + document.title,
listTypes: [ listTypes: [
{ {
title: 'Upcoming Movies', title: 'Your Requests',
shortTitle: 'Upcoming', shortTitle: 'User Requests',
query: 'upcoming', query: 'user-requests',
name: 'home-category', name: 'user-requests',
type: 'collection', type: 'user-requests',
isCategory: true isProfileContent: true
// isCategory: true,
}, },
{ {
title: 'Requested Movies & Shows', title: 'Requested Movies & Shows',
@@ -22,6 +23,14 @@ let storage = {
isCategory: true, isCategory: true,
isProfileContent: true isProfileContent: true
}, },
{
title: 'Upcoming Movies',
shortTitle: 'Upcoming',
query: 'upcoming',
name: 'home-category',
type: 'collection',
isCategory: true
},
{ {
title: 'Now Playing Movies', title: 'Now Playing Movies',
shortTitle: 'Now Playing', shortTitle: 'Now Playing',
@@ -47,15 +56,6 @@ let storage = {
title: 'Your Favorite Movies', title: 'Your Favorite Movies',
query: 'favorite', query: 'favorite',
isCategory: false isCategory: false
},
{
title: 'Your Requests',
shortTitle: 'User Requests',
query: 'user-requests',
name: 'user-requests',
type: 'user-requests',
isProfileContent: true
// isCategory: true,
} }
], ],
categories: {}, categories: {},

View File

@@ -5,7 +5,7 @@ module.exports = {
entry: './src/main.js', entry: './src/main.js',
output: { output: {
path: path.resolve(__dirname, './dist'), path: path.resolve(__dirname, './dist'),
publicPath: '/request/dist/', publicPath: '/dist/',
filename: 'build.js' filename: 'build.js'
}, },
module: { module: {