Skip to content

Commit fad9aaa

Browse files
committed
Implement chapter time over full audiobook duration
1 parent 075f73b commit fad9aaa

File tree

3 files changed

+87
-19
lines changed

3 files changed

+87
-19
lines changed

src/layouts/default/PlayerOSD/PlayerTimeline.vue

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"
1313
style="width: 100%"
1414
:min="0"
15-
:max="store.curQueueItem?.duration"
15+
:max="playerCurQueueItemDuration"
1616
hide-details
1717
:track-size="4"
1818
:thumb-size="isThumbHidden ? 0 : 10"
@@ -88,36 +88,77 @@ const isDragging = ref(false);
8888
const curTimeValue = ref(0);
8989
const tempTime = ref(0);
9090
91+
const chapterTime = computed(() =>
92+
localStorage.getItem("frontend.settings.audiobook_chapter_time") == "true"
93+
);
94+
9195
// computed properties
96+
const curChapter = computed(() => {
97+
if (store.curQueueItem?.media_item?.metadata?.chapters) {
98+
return store.curQueueItem.media_item.metadata.chapters.find((chapter) => {
99+
if (!store.activePlayerQueue?.elapsed_time) return null;
100+
if (!chapter.end) return null;
101+
return (
102+
chapter.start < store.activePlayerQueue?.elapsed_time &&
103+
chapter.end > store.activePlayerQueue?.elapsed_time
104+
);
105+
});
106+
}
107+
return null;
108+
});
109+
110+
const playerCurQueueItemDuration = computed(() => {
111+
if (
112+
chapterTime.value &&
113+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
114+
) {
115+
if (!curChapter.value?.end) return 0;
116+
return curChapter.value?.end - curChapter.value?.start;
117+
}
118+
return store.curQueueItem?.duration;
119+
});
120+
121+
const curQueueItemTime = computed(() => {
122+
if (isDragging.value) {
123+
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
124+
tempTime.value = curTimeValue.value;
125+
return curTimeValue.value;
126+
}
127+
128+
if (store.activePlayerQueue) {
129+
if (
130+
chapterTime.value &&
131+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
132+
) {
133+
if (!curChapter.value?.start) return 0;
134+
return store.activePlayerQueue?.elapsed_time - curChapter.value?.start;
135+
}
136+
return store.activePlayerQueue.elapsed_time;
137+
}
138+
return 0;
139+
});
140+
92141
const playerCurTimeStr = computed(() => {
93-
if (!store.curQueueItem) return "0:00";
142+
if (!playerCurQueueItemDuration.value || !curQueueItemTime.value) return "0:00";
94143
if (showRemainingTime.value) {
95144
return `-${formatDuration(
96-
store.curQueueItem.duration - curQueueItemTime.value,
145+
playerCurQueueItemDuration.value - curQueueItemTime.value,
97146
)}`;
98147
} else {
99148
return `${formatDuration(curQueueItemTime.value)}`;
100149
}
101150
});
151+
102152
const playerTotalTimeStr = computed(() => {
103-
if (!store.curQueueItem) return "";
104-
if (!store.curQueueItem.duration) return "";
153+
if (!playerCurQueueItemDuration.value || !store.curQueueItem) return "";
105154
if (store.curQueueItem.media_item?.media_type == MediaType.RADIO) return "";
106-
const totalSecs = store.curQueueItem.duration;
155+
const totalSecs = playerCurQueueItemDuration.value;
107156
return formatDuration(totalSecs);
108157
});
109-
const curQueueItemTime = computed(() => {
110-
if (isDragging.value) {
111-
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
112-
tempTime.value = curTimeValue.value;
113-
return curTimeValue.value;
114-
}
115-
if (store.activePlayerQueue) return store.activePlayerQueue.elapsed_time;
116-
return 0;
117-
});
118158
119159
const chapterTicks = computed(() => {
120160
const ticks: Record<number, string> = {};
161+
if (chapterTime.value) return [];
121162
if (store.curQueueItem?.media_item?.metadata?.chapters) {
122163
store.curQueueItem.media_item.metadata.chapters.forEach((chapter) => {
123164
ticks[chapter.start] = chapter.name;
@@ -141,10 +182,9 @@ const startDragging = function () {
141182
const stopDragging = () => {
142183
isDragging.value = false;
143184
if (!isDragging.value && store.activePlayer) {
144-
api.playerCommandSeek(
145-
store.activePlayer.player_id,
146-
Math.round(tempTime.value),
147-
);
185+
var seekTime = tempTime.value;
186+
if (curChapter.value?.start && chapterTime.value) seekTime = curChapter.value?.start + seekTime;
187+
api.playerCommandSeek(store.activePlayer.player_id, Math.round(seekTime));
148188
}
149189
};
150190

src/translations/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,10 @@
512512
"enable_builtin_player": {
513513
"label": "Allow playback to this device\/browser",
514514
"description": "Enable the (experimental) built-in player for this device\/browser, which allows you to stream all Music Assistant content directly to this device."
515+
},
516+
"audiobook_chapter_time": {
517+
"label": "Audiobook Chapter Progress",
518+
"description": "Show the progress of the current chapter instead of the full duration when playing audiobooks."
515519
}
516520
},
517521
"show_info": "Show info",

src/views/settings/FrontendConfig.vue

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,30 @@ onMounted(() => {
129129
localStorage.getItem("frontend.settings.enable_builtin_player") !=
130130
"false",
131131
},
132+
{
133+
key: "audiobook_chapter_time",
134+
type: ConfigEntryType.BOOLEAN,
135+
label: "chapter_time",
136+
default_value: false,
137+
required: false,
138+
multi_value: false,
139+
category: "audiobooks_podcasts",
140+
value:
141+
localStorage.getItem("frontend.settings.audiobook_chapter_time") ==
142+
"true",
143+
},
144+
{
145+
key: "audiobook_chapter_time",
146+
type: ConfigEntryType.BOOLEAN,
147+
label: "chapter_time",
148+
default_value: false,
149+
required: false,
150+
multi_value: false,
151+
category: "audiobooks_podcasts",
152+
value:
153+
localStorage.getItem("frontend.settings.audiobook_chapter_time") ==
154+
"true",
155+
},
132156
];
133157
});
134158

0 commit comments

Comments
 (0)