Audio part is added

Audio part is added
This commit is contained in:
Alexander I. Chebykin 2022-04-18 01:38:16 +03:00
parent 4f27a90e30
commit 04f0ccc13c
7 changed files with 316 additions and 41 deletions

View File

@ -23,11 +23,16 @@
</v-tabs>
<v-spacer></v-spacer>
<!--
<v-btn
:icon="theme == 'light' ? 'mdi-weather-night' : 'mdi-weather-sunny'"
@click="toggleTheme"
></v-btn>
-->
<v-btn
icon="mdi-cog-outline"
@click="showSettings"
></v-btn>
</v-app-bar>
<v-progress-linear
@ -38,13 +43,16 @@
<v-main>
<v-container fluid class="ma-0 pa-0">
<transition name="fade">
<router-view
ref="view"
@authorSelected="loadBooks"
@bookSelected="loadBook"
/>
</transition>
<router-view v-slot="{ Component }"
ref="view"
@authorSelected="loadBooks"
@bookSelected="loadBook"
@loadChapter="loadChapter"
>
<transition name="fade">
<component :is="Component"/>
</transition>
</router-view>
</v-container>
</v-main>
@ -54,20 +62,21 @@
<v-list-item height="32" min-height="32" class="pb-0 pt-8">
<v-slider
v-model="progress"
color="blue"
min="0"
max="100"
dense
hide-details
:disabled="isOff"
:inverse-label="true"
:label="`${currentTime ? currentTime : '00:00:00'}/${duration ? duration : '00:00:00'}`"
class="time-slider w-100 px-2 py-0 m-0"
></v-slider>
>
<template v-slot:append>{{ currentTime ? currentTime : "00:00"}}/{{duration ? duration : "00:00"}}</template>
</v-slider>
</v-list-item>
<v-list-item class="py-0">
<v-list-item-content v-if="smAndUp">
<v-list-item-title>{{ authorName }}</v-list-item-title>
<v-list-item-subtitle>{{ bookTitle }}<span v-if="currentChapter !== ''"> - {{ currentChapter }}</span></v-list-item-subtitle>
<v-list-item-title>{{ $store.getters.book.author }}</v-list-item-title>
<v-list-item-subtitle>{{ $store.getters.book.title }}<span v-if="$store.getters.chapters.length > 0 && currentChapter !== null && $store.getters.chapters[currentChapter].title !== ''"> - {{ $store.getters.chapters[currentChapter].title }}</span></v-list-item-subtitle>
</v-list-item-content>
<v-spacer></v-spacer>
@ -110,7 +119,9 @@
:class="{ 'mr-3': mdAndUp, 'ml-3': mdAndDown }"
:min="0"
:max="10"
thumb-label
hide-details
color="blue"
v-model="volume"
prepend-icon="mdi-volume-low"
append-icon="mdi-volume-high"
@ -138,6 +149,7 @@
</v-fab-transition>
-->
<message-box ref="MessageBox"/>
<settings-dialog ref="SettingsDialog"/>
</v-app>
</template>
@ -146,11 +158,13 @@
import { ref } from "vue";
import { useDisplay } from "vuetify";
import MessageBox from "./components/MessageBox";
import SettingsDialog from "./components/SettingsDialog";
export default {
name: "App",
components: {
MessageBox,
SettingsDialog,
},
setup () {
@ -180,15 +194,19 @@
appData: { name: "", net: "" },
backgroundImage: this.defaultBackground,
authors: [],
authors: [],
autoBookmark: true,
audioElement: null,
status: 0,
currentChapter: null,
volume: 5,
audioElement: null,
currentChapter: null,
currentProgress: 0,
currentTime: "00:00",
playbackRate: 1,
status: 0,
volume: 5,
scrolled: false,
useWikipedia: true,
scrolled: false,
useWikipedia: true,
}
},
@ -203,7 +221,26 @@
}
},
volume () { this.updateVolume(); }
"$store.getters.darkTheme" (val) {
this.setTheme(val == true ? "dark" : "light");
localStorage.darkTheme = val;
},
"$store.getters.playbackRate" (val) {
this.playbackRate = val;
localStorage.playbackRate = val;
},
playbackRate (val) {
if (this.audioElement !== null) {
this.audioElement.playbackRate = val;
}
},
volume (val) {
this.updateVolume();
localStorage.volume = val;
}
},
computed: {
@ -223,12 +260,35 @@
return (this.currentChapter !== null) && this.audioElement;
},
progress: {
get () {
return this.currentProgress;
},
set (val) {
if (this.audioElement !== null && val !== Math.round(this.currentProgress)) {
//console.log(this.audioElement);
//console.log(this.audioElement.duration);
this.currentProgress = val;
this.audioElement.currentTime = this.audioElement.duration * val / 100;
}
}
},
showProgress () {
return false;
}
},
mounted () {
let darkTheme = localStorage.darkTheme ? JSON.parse(localStorage.darkTheme) : false;
this.$store.commit("setDarkTheme", darkTheme);
let playbackRate = localStorage.playbackRate ? JSON.parse(localStorage.playbackRate) : 1;
this.$store.commit("setPlaybackRate", playbackRate);
this.volume = localStorage.volume ? JSON.parse(localStorage.volume) : 5;
this.appData.name = this.$t("appName");
this.appData.net = this.appConfig.appNet;
@ -352,6 +412,12 @@
console.log(bmKey, bookmark);
if (bookmark !== null) {
this.loadChapter(bookmark.chapter, bookmark.progress);
} else {
this.loadChapter();
}
if (this.useWikipedia) {
this.fillWikiInfo(bookData.title, false);
}
@ -412,16 +478,135 @@
})
},
loadChapter (index = 0, progress = 0, autoplay = false) {
if (this.audioElement) { this.audioElement.pause(); }
if (index >= this.$store.getters.chapters.length) { return false; } // show message?
this.currentChapter = index;
this.audioElement = new Audio(encodeURI(this.$store.getters.chapters[index].url));
this.audioElement.playbackRate = this.playbackRate;
this.$store.commit("setCurChapter", index);
this.updateVolume();
this.status = this.statuses.stopped;
this.audioElement.addEventListener("ended", this.loadNextChapter);
this.audioElement.ontimeupdate = this.updateProgress;
let component = this;
this.audioElement.addEventListener("durationchange", function () {
let min = parseInt(component.audioElement.duration / 60) < 10
? "0" + parseInt(component.audioElement.duration / 60)
: parseInt(component.audioElement.duration / 60);
let sec = parseInt(component.audioElement.duration % 60) < 10
? "0" + parseInt(component.audioElement.duration % 60)
: parseInt(component.audioElement.duration % 60);
component.duration = min + ":" + sec;
});
this.audioElement.addEventListener("canplay", function _listener () {
component.audioElement.currentTime = component.audioElement.duration * progress / 100;
component.audioElement.removeEventListener("canplay", _listener, true);
}, true);
if (autoplay) this.play();
},
loadNextChapter (autoplay = true) {
this.currentChapter++;
if (this.currentChapter >= this.$store.getters.chapters.length) {
this.currentChapter--;
return false;
}
this.loadChapter(this.currentChapter, 0, autoplay);
},
nextChapter () {
if (this.currentChapter < this.$store.getters.chapters.length - 1) {
this.currentChapter++;
this.loadChapter(this.currentChapter, 0, true);
}
},
onScroll () {
this.scrolled = window.scrollY > 0;
},
pause () {
this.status = this.statuses.paused;
this.audioElement.pause();
},
play () {
this.status = this.statuses.playing;
this.audioElement.play();
},
prevChapter () {
if (this.currentChapter > 0) {
this.currentChapter--;
this.loadChapter(this.currentChapter, 0, true);
}
},
setBookmark (silent = true) {
if (this.$store.getters.chapters.length == 0) { return; }
let data = {
chapter: this.$store.getters.curChapter,
progress: this.currentProgress
};
localStorage.setItem(
this.getHash(JSON.stringify(this.$store.getters.chapters)),
JSON.stringify(data)
);
if (!silent) { this.showMessage(this.$t("information"), this.$t("bookmarkSaved")); }
},
showMessage (title, text) {
this.$refs.MessageBox.title = title;
this.$refs.MessageBox.text = text;
this.$refs.MessageBox.dialog = true;
},
showSettings () {
this.$refs.SettingsDialog.dialog = true;
},
toggleStatus () {
if (!this.isChapterLoaded) {
this.loadChapter(this.currentChapter || 0);
}
if (!this.isPlaying) {
this.play();
} else {
this.pause();
this.setBookmark();
}
},
updateProgress () {
if (!this.audioElement || !this.audioElement.currentTime) {
return this.currentProgress = 0;
}
this.currentProgress = (this.audioElement.currentTime / this.audioElement.duration) * 100;
let min = parseInt(this.audioElement.currentTime / 60) < 10
? "0" + parseInt(this.audioElement.currentTime / 60)
: parseInt(this.audioElement.currentTime / 60);
let sec = parseInt(this.audioElement.currentTime % 60) < 10
? "0" + parseInt(this.audioElement.currentTime % 60)
: parseInt(this.audioElement.currentTime % 60);
this.currentTime = min + ":" + sec;
},
updateVolume () {
this.audioElement ? this.audioElement.volume = (this.volume / 10) : null;
}

View File

@ -123,8 +123,19 @@
data: () => ({
illustrated: false,
currentChapter: 0
})
}),
computed: {
currentChapter: function () {
return this.$store.getters.curChapter;
}
},
methods: {
loadChapter (index = 0, progress = 0, autoplay = false) {
this.$emit("loadChapter", index, progress, autoplay);
}
}
}
</script>
@ -146,5 +157,5 @@
background: transparent
.v-list-item.no-selection-pl
padding-left: 74px
padding-left: 73px
</style>

View File

@ -0,0 +1,60 @@
<template>
<v-layout row justify-center>
<v-dialog
v-model="dialog"
persistent
width="350"
>
<v-card style="width: 350px">
<v-card-title class="headline">{{ $t("settings") }}</v-card-title>
<v-card-text>
<v-switch
v-model="darkTheme"
color="blue"
:label="$t('darkTheme')"
></v-switch>
<div class="mb-7 text-caption">{{ $t("playbackRate") }}</div>
<v-slider
v-model="playbackRate"
color="blue"
label="color"
min="0.5"
max="2"
step="0.5"
show-ticks="always"
thumb-label="always"
tick-size="4"
></v-slider>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" flat @click="dialog = false">Ok</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</template>
<script>
export default {
data () {
return {
dialog: false,
darkTheme: false,
playbackRate: 1
}
},
watch: {
dialog (val) {
if (val) {
this.darkTheme = this.$store.getters.darkTheme;
this.playbackRate = this.$store.getters.playbackRate;
}
},
darkTheme (val) { this.$store.commit("setDarkTheme", val); },
playbackRate (val) { this.$store.commit("setPlaybackRate", val); }
}
}
</script>

View File

@ -2,8 +2,13 @@ const messages = {
appName: "AudioLib",
authors: "Authors",
book: "Books",
bookmarkSaved: "Bookmark saved",
books: "Book",
cycle: "Cycle"
cycle: "Cycle",
darkTheme: "DarkTheme",
information: "Information",
playbackRate: "Playback rate",
settings: "Settings"
}
export default messages;

View File

@ -2,8 +2,13 @@ const messages = {
appName: "Аудиотека",
authors: "Авторы",
book: "Книга",
bookmarkSaved: "Закладка сохранена",
books: "Книги",
cycle: "Цикл"
cycle: "Цикл",
darkTheme: "Тёмная тема",
information: "Информация",
playbackRate: "Скорость воспроизведения",
settings: "Настройки"
}
export default messages;

View File

@ -10,7 +10,6 @@ const store = createStore({
wiki: ""
},
authors: [],
books: [],
book: {
author: "",
photo: "",
@ -18,24 +17,34 @@ const store = createStore({
cover: "",
wiki: ""
},
chapters: []
books: [],
chapters: [],
curChapter: 0,
darkTheme: false,
playbackRate: 1,
}
},
getters: {
author: state => { return state.author; },
authors: state => { return state.authors; },
books: state => { return state.books; },
book: state => { return state.book; },
chapters: state => { return state.chapters }
author: state => { return state.author; },
authors: state => { return state.authors; },
book: state => { return state.book; },
books: state => { return state.books; },
chapters: state => { return state.chapters },
curChapter: state => { return state.curChapter },
darkTheme: state => { return state.darkTheme },
playbackRate: state => { return state.playbackRate },
},
mutations: {
setAuthors (state, payload) { state.authors = payload; },
setAuthor (state, payload) { state.author = payload; },
setBooks (state, payload) { state.books = payload; },
setBook (state, payload) { state.book = payload; },
setChapters (state, payload) { state.chapters = payload; }
setAuthor (state, payload) { state.author = payload; },
setAuthors (state, payload) { state.authors = payload; },
setBook (state, payload) { state.book = payload; },
setBooks (state, payload) { state.books = payload; },
setChapters (state, payload) { state.chapters = payload; },
setCurChapter (state, payload) { state.curChapter = payload; },
setDarkTheme (state, payload) { state.darkTheme = payload; },
setPlaybackRate (state, payload) { state.playbackRate = payload; },
}
});

View File

@ -5942,9 +5942,9 @@ vuetify-loader@^2.0.0-alpha.0:
upath "^2.0.1"
"vuetify@npm:@vuetify/nightly@next":
version "3.0.0-next-20220415.0"
resolved "https://registry.yarnpkg.com/@vuetify/nightly/-/nightly-3.0.0-next-20220415.0.tgz#dbcc0ae803a6efbb4eb6dbdb1c9133fcad7012ab"
integrity sha512-+KByttalgyzdE8JQmbj5sQiKCLcRJgjrdUJL2aY3c8AbLEZEwFa4ryAimkY73W/JFcUTpuw29oGiiN6BV5AFww==
version "3.0.0-next-20220416.0"
resolved "https://registry.yarnpkg.com/@vuetify/nightly/-/nightly-3.0.0-next-20220416.0.tgz#d3be97e6130900647b51ca924ae60b0812a9b5d2"
integrity sha512-3jgtwcZXmrCeR1gcLyL2jvUnZftc9RyaXHdQpXQhmlFazYEtSymsRoD8RAEv2P2GeVGKVKkR7KfBrVhUBEgaSg==
vuex@^4.0.2:
version "4.0.2"