Skip to content

Commit 2b3f74b

Browse files
committed
Implement chapter time over full audiobook duration
1 parent b3e46e9 commit 2b3f74b

File tree

3 files changed

+71
-19
lines changed

3 files changed

+71
-19
lines changed

src/layouts/default/PlayerOSD/PlayerTimeline.vue

Lines changed: 55 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="2"
1818
:thumb-size="isThumbHidden ? 0 : 10"
@@ -88,36 +88,73 @@ 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+
return (
100+
chapter.start! < store.activePlayerQueue?.elapsed_time! &&
101+
chapter.end! > store.activePlayerQueue?.elapsed_time!
102+
);
103+
});
104+
}
105+
return null;
106+
});
107+
108+
const playerCurQueueItemDuration = computed(() => {
109+
if (
110+
chapterTime.value &&
111+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
112+
) {
113+
return curChapter.value?.end! - curChapter.value?.start!;
114+
}
115+
return store.curQueueItem?.duration;
116+
});
117+
118+
const curQueueItemTime = computed(() => {
119+
if (isDragging.value) {
120+
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
121+
tempTime.value = curTimeValue.value;
122+
return curTimeValue.value;
123+
}
124+
125+
if (store.activePlayerQueue) {
126+
if (
127+
chapterTime.value &&
128+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
129+
) {
130+
return store.activePlayerQueue?.elapsed_time - curChapter.value?.start!;
131+
}
132+
return store.activePlayerQueue.elapsed_time;
133+
}
134+
return 0;
135+
});
136+
92137
const playerCurTimeStr = computed(() => {
93-
if (!store.curQueueItem) return "0:00";
138+
if (!playerCurQueueItemDuration.value || !curQueueItemTime.value) return "0:00";
94139
if (showRemainingTime.value) {
95140
return `-${formatDuration(
96-
store.curQueueItem.duration - curQueueItemTime.value,
141+
playerCurQueueItemDuration.value - curQueueItemTime.value,
97142
)}`;
98143
} else {
99144
return `${formatDuration(curQueueItemTime.value)}`;
100145
}
101146
});
147+
102148
const playerTotalTimeStr = computed(() => {
103-
if (!store.curQueueItem) return "";
104-
if (!store.curQueueItem.duration) return "";
149+
if (!playerCurQueueItemDuration.value || !store.curQueueItem) return "";
105150
if (store.curQueueItem.media_item?.media_type == MediaType.RADIO) return "";
106-
const totalSecs = store.curQueueItem.duration;
151+
const totalSecs = playerCurQueueItemDuration.value;
107152
return formatDuration(totalSecs);
108153
});
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-
});
118154
119155
const chapterTicks = computed(() => {
120156
const ticks: Record<number, string> = {};
157+
if (chapterTime.value) return [];
121158
if (store.curQueueItem?.media_item?.metadata?.chapters) {
122159
store.curQueueItem.media_item.metadata.chapters.forEach((chapter) => {
123160
ticks[chapter.start] = chapter.name;
@@ -141,10 +178,9 @@ const startDragging = function () {
141178
const stopDragging = () => {
142179
isDragging.value = false;
143180
if (!isDragging.value && store.activePlayer) {
144-
api.playerCommandSeek(
145-
store.activePlayer.player_id,
146-
Math.round(tempTime.value),
147-
);
181+
var seekTime = tempTime.value;
182+
if (chapterTime.value) seekTime = curChapter.value?.start! + seekTime;
183+
api.playerCommandSeek(store.activePlayer.player_id, Math.round(seekTime));
148184
}
149185
};
150186

src/translations/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@
516516
"enable_builtin_player": {
517517
"label": "Allow playback to this device\/browser",
518518
"description": "Enable the (experimental) built-in player for this device\/browser, which allows you to stream all Music Assistant content directly to this device."
519+
},
520+
"audiobook_chapter_time": {
521+
"label": "Audiobook Chapter Progress",
522+
"description": "Show the progress of the current chapter instead of the full duration when playing audiobooks."
519523
}
520524
},
521525
"show_info": "Show info",

src/views/settings/FrontendConfig.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ onMounted(() => {
143143
localStorage.getItem("frontend.settings.enable_builtin_player") !=
144144
"false",
145145
},
146+
{
147+
key: "audiobook_chapter_time",
148+
type: ConfigEntryType.BOOLEAN,
149+
label: "chapter_time",
150+
default_value: false,
151+
required: false,
152+
multi_value: false,
153+
category: "audiobooks_podcasts",
154+
value:
155+
localStorage.getItem("frontend.settings.audiobook_chapter_time") ==
156+
"true",
157+
},
146158
];
147159
});
148160

0 commit comments

Comments
 (0)