Commit f520a03f authored by Severin Kaderli's avatar Severin Kaderli
Browse files

Merge branch 'tvshows' into 'master'

Add support for tv shows

Closes #1

See merge request severinkaderli/movie-list!8
parents 49b43426 0ff766fd
# Movie List
This is an application for listing all of the movies on my current Kodi installtion on a Raspberry Pi. It reads the movie data from a Google Spreadsheet which gets updated every night using a Python script on the Raspberry Pi.
# Kodi Stats
This is an application for listing all of the movies and tv shows on my current Kodi installation on a Raspberry Pi. It reads the data from a Google Spreadsheet which gets updated every night using a Python script on the Raspberry Pi.
The Python script can be found in its own [repository](https://gitlab.com/severinkaderli/kodi-spreadsheet-sync).
......
{
"name": "movie-list",
"version": "1.2.4",
"version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -10299,6 +10299,11 @@
"vue-style-loader": "^4.1.0"
}
},
"vue-router": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz",
"integrity": "sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg=="
},
"vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
......
{
"name": "movie-list",
"version": "1.3.0",
"name": "kodi-stats",
"version": "2.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
......@@ -21,8 +21,9 @@
"node-sass": "^4.11.0",
"register-service-worker": "^1.5.2",
"sass-loader": "^7.1.0",
"vue-template-compiler": "^2.5.22",
"vue": "^2.5.22"
"vue": "^2.5.22",
"vue-router": "^3.0.2",
"vue-template-compiler": "^2.5.22"
},
"babel": {
"presets": [
......
......@@ -13,11 +13,11 @@ AddHandler application/x-httpd-php55 .php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /tools/
RewriteBase /kodi/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /tools/index.html [L]
RewriteRule . /kodi/index.html [L]
</IfModule>
<IfModule mod_expires.c>
......
{
"name": "Movie List",
"short_name": "Movie List",
"name": "Kodi Statistics",
"short_name": "Kodi Stats",
"start_url": "./",
"display": "standalone",
"background_color": "#FFF",
"theme_color": "#ff5252",
"description": "A list of movies.",
"description": "List of movies and tv shows from a kodi instance.",
"orientation": "portrait",
"icons": [{
"icons": [
{
"src": "./img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
......
<template>
<section class="section">
<div
class="container"
id="app"
<div>
<nav
class="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<MovieTable />
</div>
<footer class="container">
<div class="level">
<div class="level-left">
<div class="level-item">
<p>Created by <a
target="_blank"
rel="noreferrer noopener"
href="https://severinkaderli.ch"
>Severin Kaderli</a> &copy;2018</p>
</div>
<div class="container">
<div class="navbar-brand">
<div class="navbar-item">Kodi Statistics</div>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
:class="{'is-active': isNavigationOpen}"
@click="toggleNavigation"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="level-right">
<div class="level-item">Version {{version}}</div>
<div class="level-item">
<a
target="_blank"
rel="noreferrer noopener"
href="https://gitlab.com/severinkaderli/movie-list"
>
<b-icon icon="gitlab"></b-icon>
</a>
<div
:class="{'is-active': isNavigationOpen}"
class="navbar-menu"
>
<div class="navbar-start">
<router-link
class="navbar-item"
to="/movies"
>Movies</router-link>
<router-link
class="navbar-item"
to="/shows"
>TV Shows</router-link>
</div>
</div>
</div>
</footer>
</section>
</nav>
<section class="section">
<div
class="container"
id="app"
>
<router-view></router-view>
</div>
<footer class="container">
<div class="level">
<div class="level-left">
<div class="level-item">
<p>Created by <a
target="_blank"
rel="noreferrer noopener"
href="https://severinkaderli.ch"
>Severin Kaderli</a> &copy;2018</p>
</div>
</div>
<div class="level-right">
<div class="level-item">Version {{version}}</div>
<div class="level-item">
<a
target="_blank"
rel="noreferrer noopener"
href="https://gitlab.com/severinkaderli/movie-list"
>
<b-icon icon="gitlab"></b-icon>
</a>
</div>
</div>
</div>
</footer>
</section>
</div>
</template>
<script>
import MovieTable from "./components/MovieTable.vue";
export default {
name: "app",
components: {
MovieTable
},
data() {
return {
/**
......@@ -50,8 +87,19 @@ export default {
*
* @var String
*/
version: process.env.VERSION
version: process.env.VERSION,
/**
* Flag whether the navigation on mobile devices is open or not.
*
* @bar Boolean
*/
isNavigationOpen: false
};
},
methods: {
toggleNavigation() {
this.isNavigationOpen = !this.isNavigationOpen;
}
}
};
</script>
......
......@@ -14,6 +14,13 @@ const SPREADSHEET_KEY = "1mG9C35SwEPEmURsRvcF2Brl6UrvHagvjn3MMj35SbtQ";
*/
const CACHE_KEY = "movies";
/**
* The key for the cache of the shows.
*
* @type {String}
*/
const SHOW_CACHE_KEY = "shows";
/**
* How many rows to fetch from the spreadsheet.
*
......@@ -28,10 +35,19 @@ const ROWS_TO_FETCH = 1000;
*/
const API_URL = `https://sheets.googleapis.com/v4/spreadsheets/${SPREADSHEET_KEY}/values/A2:E${ROWS_TO_FETCH}?key=${API_KEY}`;
/**
* The API URL which is used for the tv shows.
*
* @type {String}
*/
const SHOW_API_URL = `https://sheets.googleapis.com/v4/spreadsheets/${SPREADSHEET_KEY}/values/'TV Shows'!A2:F${ROWS_TO_FETCH}?key=${API_KEY}`;
export default {
API_KEY,
SPREADSHEET_KEY,
CACHE_KEY,
SHOW_CACHE_KEY,
ROWS_TO_FETCH,
API_URL
API_URL,
SHOW_API_URL
};
<template>
<div class="movie-table">
<div class="level">
<div class="level-left">
<div class="level-item">
<h1 class="title">TV Shows</h1>
</div>
</div>
<div class="level-right">
<div class="level-item">
<b-input
type="search"
icon="magnify"
placeholder="Search"
v-model="keyword"
/>
</div>
<div class="level-item">
<b-switch v-model="hideWatched">Hide watched</b-switch>
</div>
</div>
</div>
<b-table
:data="filteredShows"
:mobile-cards="false"
hoverable
default-sort="title"
default-sort-direction="asc"
paginated
:per-page="perPage"
:current-page.sync="currentPage"
>
<template slot-scope="props">
<b-table-column
field="title"
label="Title"
sortable
>
{{props.row.title}}
</b-table-column>
<b-table-column
field="year"
width="1"
label="Year"
centered
sortable
>
{{props.row.year}}
</b-table-column>
<b-table-column
field="episodes"
width="1"
label="Episodes"
centered
sortable
>
{{props.row.episodes}}
</b-table-column>
<b-table-column
field="percentage"
width="1"
class="min-cell"
label="Watched"
sortable
>
{{props.row.watchedEpisodes}} / {{props.row.episodes}} ({{props.row.percentage}}%)
</b-table-column>
<b-table-column
field="imdb"
label="IMDB"
width="1"
centered
sortable
>
<a
target="_blank"
rel="noreferrer noopener"
:href="props.row.imdb"
>Link</a>
</b-table-column>
</template>
<template slot="empty">
<section class="section">
<div class="content has-text-grey has-text-centered ">
<p>
<b-icon
icon="emoticon-sad"
size="is-large"
>
</b-icon>
</p>
<p>Nothing here.</p>
</div>
</section>
</template>
</b-table>
</div>
</template>
<script>
import Configuration from "../Configuration/Configuration.js";
export default {
name: "TVShowTable",
data() {
return {
/**
* The current page of the pagination.
*
* @var Number
*/
currentPage: 1,
/**
* The number of items that should be displayed in the pagination.
*
* @var Number
*/
perPage: 50,
/**
* The arrays of the movies which were fetched.
*
* @var Array
*/
shows: [],
/**
* The current entered search keyword.
*
* @var Array
*/
keyword: "",
/**
* Whether to hide the already watched movies from the list.
*
* @var Boolean
*/
hideWatched: false
};
},
created() {
// Check if there are movies in cache
if (localStorage.getItem(Configuration.SHOW_CACHE_KEY)) {
this.shows = JSON.parse(
localStorage.getItem(Configuration.SHOW_CACHE_KEY)
);
}
this.fetchShows();
},
methods: {
async fetchShows() {
const response = await fetch(Configuration.SHOW_API_URL);
const jsonResponse = await response.json();
const rows = jsonResponse.values;
let shows = [];
rows.forEach(row => {
shows.push({
title: row[0],
year: row[1],
episodes: row[2],
watchedEpisodes: row[3],
rating: row[5],
imdb: row[5],
percentage: Math.floor((row[3] / row[2]) * 100)
});
});
this.shows = shows;
// Setting local storage
localStorage.setItem(
Configuration.SHOW_CACHE_KEY,
JSON.stringify(this.shows)
);
}
},
computed: {
filteredShows() {
let filteredShows = this.shows.filter(show => {
return (
show.title.toLowerCase().indexOf(this.keyword.toLowerCase()) >= 0
);
});
if (this.hideWatched) {
filteredShows = filteredShows.filter(show => show.percentage < 100);
}
return filteredShows;
}
}
};
</script>
<style lang="scss">
.min-cell {
white-space: nowrap;
}
</style>
import Vue from "vue";
import { Table, Field, Switch, Input, Icon } from "buefy/dist/components";
import App from "./App.vue";
import router from "./router";
import "./registerServiceWorker";
Vue.use(Field);
......@@ -10,5 +11,6 @@ Vue.use(Switch);
Vue.use(Table);
new Vue({
router,
render: h => h(App)
}).$mount("#app");
import Vue from "vue";
import Router from "vue-router";
import MovieTable from "./components/MovieTable.vue";
import TVShowTable from "./components/TVShowTable.vue";
Vue.use(Router);
export default new Router({
mode: "history",
base: "/kodi/",
linkExactActiveClass: "is-active",
routes: [
{
path: "/",
redirect: "/movies"
},
{
path: "/movies",
component: MovieTable
},
{
path: "/shows",
component: TVShowTable
}
]
});
const webpack = require("webpack");
module.exports = {
publicPath: "/movies/",
publicPath: "/kodi/",
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment