Skip to content

Commit

Permalink
feat: New scraper page UI (#814)
Browse files Browse the repository at this point in the history
* made scraper list a table

* move import scene to separate page

Co-authored-by: crwxaj <crwxaj>
  • Loading branch information
crwxaj authored Jul 4, 2022
1 parent 49176df commit 9588a02
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 121 deletions.
6 changes: 5 additions & 1 deletion ui/src/views/options/Options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<b-menu-list :label="$t('Scene data')">
<b-menu-item :label="$t('Scrapers')" :active="active==='data-scrapers'"
@click="setActive('data-scrapers')"/>
<b-menu-item :label="$t('Create/Import scene')" :active="active==='create-scene'"
@click="setActive('create-scene')"/>
<b-menu-item :label="$t('Funscripts')" :active="active==='funscripts'"
@click="setActive('funscripts')"/>
<b-menu-item :label="$t('Data import/export')" :active="active==='data-import-export'"
Expand All @@ -32,6 +34,7 @@
<Cache v-show="active==='cache'"/>
<Previews v-show="active==='previews'"/>
<SceneDataScrapers v-show="active==='data-scrapers'"/>
<SceneCreate v-show="active==='create-scene'"/>
<Funscripts v-show="active==='funscripts'"/>
<SceneDataImportExport v-show="active==='data-import-export'"/>
<InterfaceWeb v-show="active==='interface_web'"/>
Expand All @@ -48,6 +51,7 @@
import InterfaceWeb from './sections/InterfaceWeb'
import Storage from './sections/Storage'
import SceneDataScrapers from './sections/OptionsSceneDataScrapers'
import SceneCreate from './sections/OptionsSceneCreate'
import Funscripts from './sections/Funscripts'
import SceneDataImportExport from './sections/OptionsSceneDataImportExport'
import InterfaceDLNA from './sections/InterfaceDLNA.vue'
Expand All @@ -56,7 +60,7 @@ import Previews from './sections/Previews.vue'
import InterfaceDeoVR from './sections/InterfaceDeoVR.vue'
export default {
components: { Storage, SceneDataScrapers, Funscripts, SceneDataImportExport, InterfaceWeb, InterfaceDLNA, InterfaceDeoVR, Cache, Previews },
components: { Storage, SceneDataScrapers, SceneCreate, Funscripts, SceneDataImportExport, InterfaceWeb, InterfaceDLNA, InterfaceDeoVR, Cache, Previews },
data: function () {
return {
active: 'storage'
Expand Down
102 changes: 102 additions & 0 deletions ui/src/views/options/sections/OptionsSceneCreate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div class="content">
<h3 class="title">{{$t('Import JAVR scene from R18')}}</h3>
<div class="card">
<div class="card-content content">
<b-field grouped>
<b-input v-model="javrQuery" placeholder="URL or ID (XXXX-001)" type="search"></b-input>
<b-button class="button is-primary" v-on:click="scrapeJAVR()">{{$t('Go')}}</b-button>
</b-field>
</div>
</div>

<h3 class="title">{{$t('Import scene from TPDB')}}</h3>
<div class="card">
<div class="card-content content">
<h5 class="title">API Token</h5>
<b-field label="TPDB API Token" label-position="on-border" grouped>
<b-input v-model="tpdbApiToken" placeholder="TPDB API Token" type="search"></b-input>
</b-field>
<br>
<b-field label="TPDB Scene URL" label-position="on-border" grouped>
<b-input v-model="tpdbSceneUrl" placeholder="TPDB URL" type="search"></b-input>
<b-button class="button is-primary" v-on:click="scrapeTPDB()">{{$t('Go')}}</b-button>
</b-field>
</div>
</div>

<h3 class="title">{{$t('Create custom scene')}}</h3>
<div class="card">
<div class="card-content content">
<b-field label="Scene title" label-position="on-border">
<b-input v-model="customSceneTitle" placeholder="Stepsis stuck in washing machine" type="search"></b-input>
</b-field>
<b-field label="Scene ID" label-position="on-border" grouped>
<b-input v-model="customSceneID" placeholder="Can be empty" type="search"></b-input>
<b-button class="button is-primary" v-on:click="addScene()">{{$t('Go')}}</b-button>
</b-field>
</div>
</div>
</div>
</template>

<script>
import ky from 'ky'
export default {
name: 'OptionsCreateScene',
data () {
return {
javrQuery: '',
tpdbSceneUrl: ''
}
},
mounted () {
this.$store.dispatch('optionsVendor/load')
},
methods: {
addScene() {
if (this.customSceneTitle !== '') {
ky.post('/api/scene/create', { json: { title: this.customSceneTitle, id: this.customSceneID } })
}
},
scrapeJAVR () {
ky.post('/api/task/scrape-javr', { json: { q: this.javrQuery } })
},
scrapeTPDB () {
ky.post('/api/task/scrape-tpdb', {
json: { apiToken: this.tpdbApiToken, sceneUrl: this.tpdbSceneUrl }
})
},
},
computed: {
tpdbApiToken: {
get () {
return this.$store.state.optionsVendor.tpdb.apiToken
},
set (value) {
this.$store.state.optionsVendor.tpdb.apiToken = value
}
}
}
}
</script>

<style scoped>
.card {
overflow: visible;
height: 100%;
}
.card-content {
padding-top: 1em;
padding-left: 1em;
}
</style>

<style>
.content table td.narrow{
padding-top: 5px;
padding-bottom: 2px;
}
</style>
201 changes: 81 additions & 120 deletions ui/src/views/options/sections/OptionsSceneDataScrapers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,60 @@
<div class="content">
<div class="columns">
<div class="column">
<h3 class="title">{{$t('Mainstream sites')}}</h3>
<h3 class="title">{{$t('Scrape scenes from studios')}}</h3>
</div>
<div class="column buttons" align="right">
<a class="button is-primary" v-on:click="taskScrape('_enabled')">{{$t('Run selected scrapers')}}</a>
</div>
</div>
<div class="columns is-multiline">
<div class="column is-multiline is-one-third" v-for="item in items" :key="item.id">
<div :class="[runningScrapers.includes(item.id) ? 'card running' : 'card']">
<div class="card-content content">
<p class="image is-32x32 is-pulled-left avatar">
<b-table :data="scraperList">
<b-table-column field="is_enabled" :label="$t('Enabled')" v-slot="props" width="60" sortable>
<span><b-switch v-model ="props.row.is_enabled" @input="$store.dispatch('optionsSites/toggleSite', {id: props.row.id})"/></span>
</b-table-column>
<b-table-column field="icon" width="50" v-slot="props" cell-class="narrow">
<span class="image is-32x32">
<vue-load-image>
<img slot="image" :src="getImageURL(item.avatar_url ? item.avatar_url : '/ui/images/blank.png')"/>
<img slot="image" :src="getImageURL(props.row.avatar_url ? props.row.avatar_url : '/ui/images/blank.png')"/>
<img slot="preloader" src="/ui/images/blank.png"/>
<img slot="error" src="/ui/images/blank.png"/>
</vue-load-image>
</p>

<h5 class="title">{{item.name}}</h5>
<p :class="[runningScrapers.includes(item.id) ? 'invisible' : '']">
<small v-if="item.last_update !== '0001-01-01T00:00:00Z'">
Updated {{formatDistanceToNow(parseISO(item.last_update))}} ago</small>
<small v-else>{{$t('Never scraped')}}</small>
</p>
<p :class="[runningScrapers.includes(item.id) ? '' : 'invisible']">
<small>{{$t('Scraping now...')}}</small>
</p>
<div class="switch">
<b-switch :value="item.is_enabled" @input="$store.dispatch('optionsSites/toggleSite', {id: item.id})"/>
</div>
<div class="menu">
<b-dropdown aria-role="list" class="is-pulled-right" position="is-bottom-left">
<template slot="trigger">
<b-icon icon="dots-vertical"></b-icon>
</template>
<b-dropdown-item aria-role="listitem" @click="taskScrape(item.id)">
{{$t('Run this scraper')}}
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" @click="forceSiteUpdate(item.name)">
{{$t('Force update scenes')}}
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" @click="deleteScenes(item.name)">
{{$t('Delete scraped scenes')}}
</b-dropdown-item>
</b-dropdown>
</div>
</div>
</div>
</div>
</div>

<div class="columns is-multiline">
<div class="column is-multiline is-one-third">
<h3 class="title">{{$t('JAVR scraper')}}</h3>
<div class="card">
<div class="card-content content">
<h5 class="title">R18</h5>
<b-field grouped>
<b-input v-model="javrQuery" placeholder="URL or ID (XXXX-001)" type="search"></b-input>
<b-button class="button is-primary" v-on:click="scrapeJAVR()">{{$t('Go')}}</b-button>
</b-field>
</div>
</span>
</b-table-column>
<b-table-column field="sitename" :label="$t('Studio')" sortable searchable v-slot="props">
{{ props.row.sitename }}
</b-table-column>
<b-table-column field="source" :label="$t('Source')" sortable searchable v-slot="props">
{{ props.row.source }}
</b-table-column>
<b-table-column field="last_update" :label="$t('Last scrape')" sortable v-slot="props">
<span :class="[runningScrapers.includes(props.row.id) ? 'invisible' : '']">
<span v-if="props.row.last_update !== '0001-01-01T00:00:00Z'">
{{formatDistanceToNow(parseISO(props.row.last_update))}} ago</span>
<span v-else>{{$t('Never scraped')}}</span>
</span>
<span :class="[runningScrapers.includes(props.row.id) ? '' : 'invisible']">
<span class="pulsate is-info">{{$t('Scraping now...')}}</span>
</span>
</b-table-column>
<b-table-column field="options" :label="opt" v-slot="props" width="30">
<div class="menu">
<b-dropdown aria-role="list" class="is-pulled-right" position="is-bottom-left">
<template slot="trigger">
<b-icon icon="dots-vertical mdi-18px"></b-icon>
</template>
<b-dropdown-item aria-role="listitem" @click="taskScrape(props.row.id)">
{{$t('Run this scraper')}}
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" @click="forceSiteUpdate(props.row.name)">
{{$t('Force update scenes')}}
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" @click="deleteScenes(props.row.name)">
{{$t('Delete scraped scenes')}}
</b-dropdown-item>
</b-dropdown>
</div>
</div>

<div class="column is-multiline is-one-third">
<h3 class="title">{{$t('TPDB scraper')}}</h3>
<div class="card">
<div class="card-content content">
<h5 class="title">API Token</h5>
<b-input v-model="tpdbApiToken" placeholder="TPDB API Token" type="search"></b-input>
<br>
<h5 class="title">TPDB Scene URL</h5>
<b-field grouped>
<b-input v-model="tpdbSceneUrl" placeholder="TPDB URL" type="search"></b-input>
<b-button class="button is-primary" v-on:click="scrapeTPDB()">{{$t('Go')}}</b-button>
</b-field>
</div>
</div>
</div>

<div class="column is-multiline is-one-third">
<h3 class="title">{{$t('Custom scene')}}</h3>
<div class="card">
<div class="card-content content">
<b-field label="Scene title" label-position="on-border">
<b-input v-model="customSceneTitle" placeholder="Stepsis stuck in washing machine" type="search"></b-input>
</b-field>
<b-field label="Scene ID" label-position="on-border">
<b-input v-model="customSceneID" placeholder="Can be empty" type="search"></b-input>
</b-field>
<b-field label-position="on-border">
<b-button class="button is-primary" v-on:click="addScene()">{{$t('Add')}}</b-button>
</b-field>
</div>
</div>
</div>
</div>
</b-table-column>
</b-table>
</div>
</template>

Expand All @@ -119,7 +75,6 @@ export default {
},
mounted () {
this.$store.dispatch('optionsSites/load')
this.$store.dispatch('optionsVendor/load')
},
methods: {
getImageURL (u) {
Expand All @@ -129,11 +84,6 @@ export default {
return u
}
},
addScene() {
if (this.customSceneTitle !== '') {
ky.post('/api/scene/create', { json: { title: this.customSceneTitle, id: this.customSceneID } })
}
},
taskScrape (site) {
ky.get(`/api/task/scrape?site=${site}`)
},
Expand Down Expand Up @@ -161,32 +111,31 @@ export default {
sanitizeSiteName(site) {
return site.split('(')[0].trim();
},
scrapeJAVR () {
ky.post('/api/task/scrape-javr', { json: { q: this.javrQuery } })
},
scrapeTPDB () {
ky.post('/api/task/scrape-tpdb', {
json: { apiToken: this.tpdbApiToken, sceneUrl: this.tpdbSceneUrl }
})
},
parseISO,
formatDistanceToNow
},
computed: {
scraperList() {
var items = this.$store.state.optionsSites.items;
let re = /(.*)\s+\((.+)\)$/;
for (let i=0; i < items.length; i++) {
items[i].sitename = items[i].name;
items[i].source = "";
var m = re.exec(items[i].name);
if (m) {
items[i].sitename = m[1];
items[i].source = m[2];
}
}
return items;
},
items () {
return this.$store.state.optionsSites.items
},
runningScrapers () {
this.$store.dispatch('optionsSites/load')
return this.$store.state.messages.runningScrapers
},
tpdbApiToken: {
get () {
return this.$store.state.optionsVendor.tpdb.apiToken
},
set (value) {
this.$store.state.optionsVendor.tpdb.apiToken = value
}
}
}
}
Expand Down Expand Up @@ -220,19 +169,31 @@ export default {
margin-bottom: 0.25em !important;
}
.switch {
position: absolute;
bottom: 0.25em;
right: 0em;
}
.invisible {
display: none;
}
.pulsate {
-webkit-animation: pulsate 0.8s linear;
-webkit-animation-iteration-count: infinite;
opacity: 0.5;
}
@-webkit-keyframes pulsate {
0% {
opacity: 0.5;
}
50% {
opacity: 1.0;
}
100% {
opacity: 0.5;
}
}
</style>

.menu {
position: absolute;
top: 0.75em;
right: 0.5em;
<style>
.content table td.narrow{
padding-top: 5px;
padding-bottom: 2px;
}
</style>

0 comments on commit 9588a02

Please sign in to comment.