diff --git a/assets/components/errors/not-found.vue b/assets/components/errors/not-found.vue
deleted file mode 100644
index 97b225e3..00000000
--- a/assets/components/errors/not-found.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
404 - Not Found
-
-
diff --git a/assets/components/home/home.vue b/assets/components/home/home.vue
index 22e45e65..313c91b7 100644
--- a/assets/components/home/home.vue
+++ b/assets/components/home/home.vue
@@ -65,112 +65,7 @@
:key="release.id"
class="scene"
>
-
-
- {{ release.site.name }}
-
- {{ formatDate(release.date, 'MMM D, YYYY') }}
-
- {{ `(${formatDate(release.dateAdded, 'MMM D, YYYY')})` }}`
-
-
-
-
-
-
-
- No thumbnail available
-
-
-
-
+
@@ -178,15 +73,17 @@
@@ -252,145 +147,4 @@ export default {
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
grid-gap: 1rem;
}
-
-.scene {
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- padding: 0 0 .5rem 0;;
- border-radius: .25rem;
- overflow: hidden;
- box-shadow: 0 0 3px rgba(0, 0, 0, .25);
-}
-
-.scene-banner {
- position: relative;
- margin: 0 0 .5rem 0;
-}
-
-.scene-thumbnail {
- width: 100%;
- height: 200px;
- display: flex;
- justify-content: center;
- align-items: center;
- object-fit: cover;
- background-position: center;
- background-size: cover;
- background-color: $shadow-hint;
- color: $shadow;
- text-shadow: 1px 1px 0 $highlight;
-}
-
-.scene-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- box-sizing: border-box;
- padding: 0 .5rem;
- margin: 0 0 .25rem 0;
-}
-
-.scene-details {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: absolute;
-}
-
-.scene-site,
-.scene-date {
- color: #fff;
- background: rgba(0, 0, 0, .5);
- font-size: .8rem;
- padding: .25rem;
- text-decoration: none;
-}
-
-.scene-site {
- border-radius: 0 0 .25rem 0;
- font-weight: bold;
-}
-
-.scene-date {
- border-radius: 0 0 0 .25rem;
-}
-
-.scene-info {
- flex-grow: 1;
-}
-
-.scene-link {
- text-decoration: none;
-}
-
-.scene-title {
- color: $text;
- margin: 0;
- font-size: 1rem;
- word-wrap: break-word;
- overflow: hidden;
- max-height: 3rem;
- line-height: 1.5rem;
-}
-
-.scene-network {
- color: #555;
- margin: 0 .25rem 0 0;
- font-size: .8rem;
-}
-
-.scene-actors {
- word-wrap: break-word;
- overflow: hidden;
- max-height: 2.5rem;
- line-height: 1.25rem;
-}
-
-.scene-tags {
- word-wrap: break-word;
- overflow: hidden;
- max-height: 2.5rem;
- line-height: 1.25rem;
-}
-
-.scene-actor,
-.scene-tag {
- margin: 0 .25rem 0 0;
-}
-
-.scene-actor {
- font-size: .9rem;
-}
-
-.scene-tag {
- font-size: .75rem;
-}
-
-.scene-actor:not(:last-of-type)::after,
-.scene-tag:not(:last-child):after {
- content: ",";
-}
-
-.actor-link,
-.tag-link {
- text-decoration: none;
-
- &:hover {
- color: $primary;
- }
-}
-
-.actor-link {
- color: $link;
-}
-
-.tag-link {
- color: $shadow-strong;
-}
-
-.thumbnail {
- width: 300px;
-}
diff --git a/assets/components/release/release.vue b/assets/components/release/release.vue
index b5a001fc..9eec9318 100644
--- a/assets/components/release/release.vue
+++ b/assets/components/release/release.vue
@@ -78,17 +78,14 @@
{{ release.network.name }}:
- {{ release.network.name }}:
-
{{ Math.floor(release.duration / 3600) }}:
{{ Math.floor((release.duration % 3600) / 60).toString().padStart(2, '0') }}:
{{ (release.duration % 60).toString().padStart(2, '0') }}
diff --git a/assets/css/_theme.scss b/assets/css/_theme.scss
index d0fc10d1..9c8e6743 100644
--- a/assets/css/_theme.scss
+++ b/assets/css/_theme.scss
@@ -1,6 +1,7 @@
/* $primary: #ff886c; */
$primary: #ff6c88;
+$background: #fff;
$text: #222;
$text-contrast: #fff;
diff --git a/assets/css/style.scss b/assets/css/style.scss
index 2d6d2a33..6820f9d3 100644
--- a/assets/css/style.scss
+++ b/assets/css/style.scss
@@ -26,3 +26,11 @@ body {
color: $primary;
margin: 0 0 1rem 0;
}
+
+.icon.icon-href {
+ fill: $shadow;
+
+ :hover {
+ fill: $primary;
+ }
+}
diff --git a/assets/js/releases/state.js b/assets/js/releases/state.js
index 427473f0..ff8b4c56 100644
--- a/assets/js/releases/state.js
+++ b/assets/js/releases/state.js
@@ -1,4 +1 @@
-export default {
- authenticated: false,
- user: null,
-};
+export default {};
diff --git a/assets/js/router.js b/assets/js/router.js
index 1fa4b894..8beb25ac 100644
--- a/assets/js/router.js
+++ b/assets/js/router.js
@@ -3,8 +3,10 @@ import VueRouter from 'vue-router';
import Home from '../components/home/home.vue';
import Release from '../components/release/release.vue';
+import Site from '../components/site/site.vue';
+import Network from '../components/network/network.vue';
import Actor from '../components/actor/actor.vue';
-import NotFound from '../components/errors/not-found.vue';
+import NotFound from '../components/errors/404.vue';
Vue.use(VueRouter);
@@ -29,6 +31,16 @@ const routes = [
component: Actor,
name: 'actor',
},
+ {
+ path: '/site/:siteSlug',
+ component: Site,
+ name: 'site',
+ },
+ {
+ path: '/network/:networkSlug',
+ component: Network,
+ name: 'network',
+ },
{
path: '*',
component: NotFound,
diff --git a/assets/js/store.js b/assets/js/store.js
index ed3317ce..755a9856 100644
--- a/assets/js/store.js
+++ b/assets/js/store.js
@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import initAuthStore from './auth/auth';
import initReleasesStore from './releases/releases';
+import initNetworksStore from './networks/networks';
function initStore(router) {
Vue.use(Vuex);
@@ -11,6 +12,7 @@ function initStore(router) {
store.registerModule('auth', initAuthStore(store, router));
store.registerModule('releases', initReleasesStore(store, router));
+ store.registerModule('networks', initNetworksStore(store, router));
return store;
}
diff --git a/config/default.js b/config/default.js
index 8dced402..79d9acf2 100644
--- a/config/default.js
+++ b/config/default.js
@@ -79,7 +79,7 @@ module.exports = {
width: 30,
},
],
- photoPath: '/mnt/stor/Pictures/traxxx',
+ photoPath: './',
filename: {
dateFormat: 'DD-MM-YYYY',
actorsJoin: ', ',
diff --git a/public/css/style.css b/public/css/style.css
index 553d9409..30d3c2fe 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -1,3 +1,123 @@
+/* $primary: #ff886c; */
+.tile[data-v-3abcf101] {
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+ padding: 0 0 .5rem 0;
+ border-radius: .25rem;
+ overflow: hidden;
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
+ height: 100%;
+}
+.banner[data-v-3abcf101] {
+ position: relative;
+ margin: 0 0 .5rem 0;
+}
+.thumbnail[data-v-3abcf101] {
+ width: 100%;
+ height: 200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ -o-object-fit: cover;
+ object-fit: cover;
+ background-position: center;
+ background-size: cover;
+ background-color: rgba(0, 0, 0, 0.1);
+ color: rgba(0, 0, 0, 0.5);
+ text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
+}
+.row[data-v-3abcf101] {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-sizing: border-box;
+ padding: 0 .5rem;
+ margin: 0 0 .25rem 0;
+}
+.details[data-v-3abcf101] {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ position: absolute;
+}
+.site[data-v-3abcf101],
+.date[data-v-3abcf101] {
+ color: #fff;
+ background: rgba(0, 0, 0, 0.5);
+ font-size: .8rem;
+ padding: .25rem;
+ text-decoration: none;
+}
+.site[data-v-3abcf101] {
+ border-radius: 0 0 .25rem 0;
+ font-weight: bold;
+}
+.date[data-v-3abcf101] {
+ border-radius: 0 0 0 .25rem;
+}
+.info[data-v-3abcf101] {
+ flex-grow: 1;
+}
+.link[data-v-3abcf101] {
+ text-decoration: none;
+}
+.title[data-v-3abcf101] {
+ color: #222;
+ margin: 0;
+ font-size: 1rem;
+ word-wrap: break-word;
+ overflow: hidden;
+ max-height: 3rem;
+ line-height: 1.5rem;
+}
+.network[data-v-3abcf101] {
+ color: #555;
+ margin: 0 .25rem 0 0;
+ font-size: .8rem;
+}
+.actors[data-v-3abcf101] {
+ word-wrap: break-word;
+ overflow: hidden;
+ max-height: 2.5rem;
+ line-height: 1.25rem;
+}
+.tags[data-v-3abcf101] {
+ word-wrap: break-word;
+ overflow: hidden;
+ max-height: 2.5rem;
+ line-height: 1.25rem;
+}
+.actor[data-v-3abcf101],
+.tag[data-v-3abcf101] {
+ margin: 0 .25rem 0 0;
+}
+.actor[data-v-3abcf101] {
+ font-size: .9rem;
+}
+.tag[data-v-3abcf101] {
+ font-size: .75rem;
+}
+.actor[data-v-3abcf101]:not(:last-of-type)::after,
+.tag[data-v-3abcf101]:not(:last-child):after {
+ content: ",";
+}
+.actor-link[data-v-3abcf101],
+.tag-link[data-v-3abcf101] {
+ text-decoration: none;
+}
+.actor-link[data-v-3abcf101]:hover,
+ .tag-link[data-v-3abcf101]:hover {
+ color: #ff6c88;
+}
+.actor-link[data-v-3abcf101] {
+ color: #cc4466;
+}
+.tag-link[data-v-3abcf101] {
+ color: rgba(0, 0, 0, 0.7);
+}
+
/* $primary: #ff886c; */
.filters-bar[data-v-5533e378] {
display: block;
@@ -38,126 +158,6 @@
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
grid-gap: 1rem;
}
-.scene[data-v-5533e378] {
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- padding: 0 0 .5rem 0;
- border-radius: .25rem;
- overflow: hidden;
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.25);
-}
-.scene-banner[data-v-5533e378] {
- position: relative;
- margin: 0 0 .5rem 0;
-}
-.scene-thumbnail[data-v-5533e378] {
- width: 100%;
- height: 200px;
- display: flex;
- justify-content: center;
- align-items: center;
- -o-object-fit: cover;
- object-fit: cover;
- background-position: center;
- background-size: cover;
- background-color: rgba(0, 0, 0, 0.1);
- color: rgba(0, 0, 0, 0.5);
- text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
-}
-.scene-row[data-v-5533e378] {
- display: flex;
- justify-content: space-between;
- align-items: center;
- box-sizing: border-box;
- padding: 0 .5rem;
- margin: 0 0 .25rem 0;
-}
-.scene-details[data-v-5533e378] {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: absolute;
-}
-.scene-site[data-v-5533e378],
-.scene-date[data-v-5533e378] {
- color: #fff;
- background: rgba(0, 0, 0, 0.5);
- font-size: .8rem;
- padding: .25rem;
- text-decoration: none;
-}
-.scene-site[data-v-5533e378] {
- border-radius: 0 0 .25rem 0;
- font-weight: bold;
-}
-.scene-date[data-v-5533e378] {
- border-radius: 0 0 0 .25rem;
-}
-.scene-info[data-v-5533e378] {
- flex-grow: 1;
-}
-.scene-link[data-v-5533e378] {
- text-decoration: none;
-}
-.scene-title[data-v-5533e378] {
- color: #222;
- margin: 0;
- font-size: 1rem;
- word-wrap: break-word;
- overflow: hidden;
- max-height: 3rem;
- line-height: 1.5rem;
-}
-.scene-network[data-v-5533e378] {
- color: #555;
- margin: 0 .25rem 0 0;
- font-size: .8rem;
-}
-.scene-actors[data-v-5533e378] {
- word-wrap: break-word;
- overflow: hidden;
- max-height: 2.5rem;
- line-height: 1.25rem;
-}
-.scene-tags[data-v-5533e378] {
- word-wrap: break-word;
- overflow: hidden;
- max-height: 2.5rem;
- line-height: 1.25rem;
-}
-.scene-actor[data-v-5533e378],
-.scene-tag[data-v-5533e378] {
- margin: 0 .25rem 0 0;
-}
-.scene-actor[data-v-5533e378] {
- font-size: .9rem;
-}
-.scene-tag[data-v-5533e378] {
- font-size: .75rem;
-}
-.scene-actor[data-v-5533e378]:not(:last-of-type)::after,
-.scene-tag[data-v-5533e378]:not(:last-child):after {
- content: ",";
-}
-.actor-link[data-v-5533e378],
-.tag-link[data-v-5533e378] {
- text-decoration: none;
-}
-.actor-link[data-v-5533e378]:hover,
- .tag-link[data-v-5533e378]:hover {
- color: #ff6c88;
-}
-.actor-link[data-v-5533e378] {
- color: #cc4466;
-}
-.tag-link[data-v-5533e378] {
- color: rgba(0, 0, 0, 0.7);
-}
-.thumbnail[data-v-5533e378] {
- width: 300px;
-}
/* $primary: #ff886c; */
.banner[data-v-2bc41e74] {
@@ -226,6 +226,52 @@
color: #ff6c88;
}
+/* $primary: #ff886c; */
+.network[data-v-757c14c2] {
+ display: flex;
+ padding: 1rem;
+ overflow: hidden;
+}
+.header[data-v-757c14c2] {
+ display: flex;
+ justify-content: space-between;
+}
+.title[data-v-757c14c2] {
+ display: inline-block;
+ margin: 0 .5rem 0 0;
+}
+.heading[data-v-757c14c2] {
+ padding: 0;
+ margin: 1rem 0;
+}
+.logo[data-v-757c14c2] {
+ height: 3rem;
+}
+.scenes[data-v-757c14c2] {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
+ grid-gap: 1rem;
+}
+
+/* $primary: #ff886c; */
+.errorpage[data-v-29109daf] {
+ background: #fff;
+ color: #ff6c88;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 2rem;
+}
+.error[data-v-29109daf] {
+ margin: 0;
+}
+.home[data-v-29109daf] {
+ color: rgba(0, 0, 0, 0.5);
+ margin: 3rem 0;
+}
+
/* $primary: #ff886c; */
.noselect {
user-select: none;
@@ -254,6 +300,11 @@ body {
color: #ff6c88;
margin: 0 0 1rem 0; }
+.icon.icon-href {
+ fill: rgba(0, 0, 0, 0.5); }
+ .icon.icon-href :hover {
+ fill: #ff6c88; }
+
/* $primary: #ff886c; */
.header[data-v-10b7ec04] {
color: #fff;
diff --git a/src/releases.js b/src/releases.js
index 877f8e8b..4996fb1e 100644
--- a/src/releases.js
+++ b/src/releases.js
@@ -41,18 +41,21 @@ async function curateRelease(release) {
site: {
id: release.site_id,
name: release.site_name,
+ slug: release.site_slug,
url: release.site_url,
},
studio: release.studio_id
? {
id: release.studio_id,
name: release.studio_name,
+ slug: release.studio_slug,
url: release.studio_url,
}
: null,
network: {
id: release.network_id,
name: release.network_name,
+ slug: release.network_slug,
url: release.network_url,
},
};
@@ -63,14 +66,48 @@ function curateReleases(releases) {
}
async function fetchReleases(releaseId) {
- // const thumbnails = await fs.readdir(path.join(config.thumbnailPath, release.site.id.toString(), release.id.toString()));
-
const releases = await knex('releases')
.where(releaseId ? { 'releases.id': releaseId } : {})
.select(
- 'releases.*', 'sites.name as site_name', 'sites.url as site_url', 'sites.network_id',
- 'studios.name as studio_name', 'studios.url as studio_url',
- 'networks.name as network_name', 'networks.url as network_url',
+ 'releases.*', 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id',
+ 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url',
+ 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url',
+ )
+ .leftJoin('sites', 'releases.site_id', 'sites.id')
+ .leftJoin('studios', 'releases.studio_id', 'studios.id')
+ .leftJoin('networks', 'sites.network_id', 'networks.id')
+ .orderBy([{ column: 'date', order: 'desc' }, { column: 'created_at', order: 'desc' }])
+ .limit(100);
+
+ return curateReleases(releases);
+}
+
+async function fetchSiteReleases(siteId, siteSlug) {
+ const releases = await knex('releases')
+ .where({ 'sites.id': siteId })
+ .orWhere({ 'sites.slug': siteSlug })
+ .select(
+ 'releases.*', 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id',
+ 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url',
+ 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url',
+ )
+ .leftJoin('sites', 'releases.site_id', 'sites.id')
+ .leftJoin('studios', 'releases.studio_id', 'studios.id')
+ .leftJoin('networks', 'sites.network_id', 'networks.id')
+ .orderBy([{ column: 'date', order: 'desc' }, { column: 'created_at', order: 'desc' }])
+ .limit(100);
+
+ return curateReleases(releases);
+}
+
+async function fetchNetworkReleases(networkId, networkSlug) {
+ const releases = await knex('releases')
+ .where({ 'networks.id': networkId })
+ .orWhere({ 'networks.slug': networkSlug })
+ .select(
+ 'releases.*', 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id',
+ 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url',
+ 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url',
)
.leftJoin('sites', 'releases.site_id', 'sites.id')
.leftJoin('studios', 'releases.studio_id', 'studios.id')
@@ -83,4 +120,6 @@ async function fetchReleases(releaseId) {
module.exports = {
fetchReleases,
+ fetchSiteReleases,
+ fetchNetworkReleases,
};
diff --git a/src/web/releases.js b/src/web/releases.js
index 44dcab07..9012d6e2 100644
--- a/src/web/releases.js
+++ b/src/web/releases.js
@@ -1,6 +1,6 @@
'use strict';
-const { fetchReleases } = require('../releases');
+const { fetchReleases, fetchNetworkReleases, fetchSiteReleases } = require('../releases');
async function fetchReleasesApi(req, res) {
const releases = await fetchReleases(req.params.releaseId);
@@ -8,6 +8,26 @@ async function fetchReleasesApi(req, res) {
res.send(releases);
}
+async function fetchNetworkReleasesApi(req, res) {
+ const networkId = typeof req.params.networkId === 'number' ? req.params.networkId : null;
+ const networkSlug = typeof req.params.networkId === 'string' ? req.params.networkId : null;
+
+ const releases = await fetchNetworkReleases(networkId, networkSlug);
+
+ res.send(releases);
+}
+
+async function fetchSiteReleasesApi(req, res) {
+ const siteId = typeof req.params.siteId === 'number' ? req.params.siteId : null;
+ const siteSlug = typeof req.params.siteId === 'string' ? req.params.siteId : null;
+
+ const releases = await fetchSiteReleases(siteId, siteSlug);
+
+ res.send(releases);
+}
+
module.exports = {
fetchReleases: fetchReleasesApi,
+ fetchNetworkReleases: fetchNetworkReleasesApi,
+ fetchSiteReleases: fetchSiteReleasesApi,
};
diff --git a/src/web/server.js b/src/web/server.js
index 2fc9e8eb..caba17b8 100644
--- a/src/web/server.js
+++ b/src/web/server.js
@@ -6,7 +6,9 @@ const express = require('express');
const Router = require('express-promise-router');
const bodyParser = require('body-parser');
-const { fetchReleases } = require('./releases');
+const { fetchReleases, fetchNetworkReleases, fetchSiteReleases } = require('./releases');
+const { fetchNetworks, fetchNetworksFromReleases } = require('./networks');
+const { fetchSites } = require('./sites');
function initServer() {
const app = express();
@@ -18,6 +20,15 @@ function initServer() {
router.get('/api/releases', fetchReleases);
router.get('/api/releases/:releaseId', fetchReleases);
+ router.get('/api/releases/networks', fetchNetworksFromReleases);
+
+ router.get('/api/networks', fetchNetworks);
+ router.get('/api/networks/:networkId', fetchNetworks);
+ router.get('/api/networks/:networkId/releases', fetchNetworkReleases);
+
+ router.get('/api/sites', fetchSites);
+ router.get('/api/sites/:siteId', fetchSites);
+ router.get('/api/sites/:siteId/releases', fetchSiteReleases);
router.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../../public/index.html'));