Skip to content

Commit 205819d

Browse files
committed
Implement chapter time over full audiobook duration
1 parent bff8f5d commit 205819d

File tree

3 files changed

+77
-19
lines changed

3 files changed

+77
-19
lines changed

src/layouts/default/PlayerOSD/PlayerTimeline.vue

Lines changed: 61 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,79 @@ const isDragging = ref(false);
8888
const curTimeValue = ref(0);
8989
const tempTime = ref(0);
9090
91+
const chapterTime = computed(() => {
92+
return (
93+
localStorage.getItem("frontend.settings.audiobook_chapter_time") == "true"
94+
);
95+
});
96+
9197
// computed properties
98+
const curChapter = computed(() => {
99+
if (store.curQueueItem?.media_item?.metadata?.chapters) {
100+
return store.curQueueItem.media_item.metadata.chapters.find((chapter) => {
101+
if (!store.activePlayerQueue?.elapsed_time) return null;
102+
if (!chapter.end) return null;
103+
return (
104+
chapter.start < store.activePlayerQueue?.elapsed_time &&
105+
chapter.end > store.activePlayerQueue?.elapsed_time
106+
);
107+
});
108+
}
109+
return null;
110+
});
111+
112+
const playerCurQueueItemDuration = computed(() => {
113+
if (
114+
chapterTime.value &&
115+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
116+
) {
117+
if (!curChapter.value?.end) return 0;
118+
return curChapter.value?.end - curChapter.value?.start;
119+
}
120+
return store.curQueueItem?.duration;
121+
});
122+
123+
const curQueueItemTime = computed(() => {
124+
if (isDragging.value) {
125+
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
126+
tempTime.value = curTimeValue.value;
127+
return curTimeValue.value;
128+
}
129+
130+
if (store.activePlayerQueue) {
131+
if (
132+
chapterTime.value &&
133+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
134+
) {
135+
if (!curChapter.value?.start) return 0;
136+
return store.activePlayerQueue?.elapsed_time - curChapter.value?.start;
137+
}
138+
return store.activePlayerQueue.elapsed_time;
139+
}
140+
return 0;
141+
});
142+
92143
const playerCurTimeStr = computed(() => {
93-
if (!store.curQueueItem) return "0:00";
144+
if (!playerCurQueueItemDuration.value || !curQueueItemTime.value) return "0:00";
94145
if (showRemainingTime.value) {
95146
return `-${formatDuration(
96-
store.curQueueItem.duration - curQueueItemTime.value,
147+
playerCurQueueItemDuration.value - curQueueItemTime.value,
97148
)}`;
98149
} else {
99150
return `${formatDuration(curQueueItemTime.value)}`;
100151
}
101152
});
153+
102154
const playerTotalTimeStr = computed(() => {
103-
if (!store.curQueueItem) return "";
104-
if (!store.curQueueItem.duration) return "";
155+
if (!playerCurQueueItemDuration.value || !store.curQueueItem) return "";
105156
if (store.curQueueItem.media_item?.media_type == MediaType.RADIO) return "";
106-
const totalSecs = store.curQueueItem.duration;
157+
const totalSecs = playerCurQueueItemDuration.value;
107158
return formatDuration(totalSecs);
108159
});
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-
});
118160
119161
const chapterTicks = computed(() => {
120162
const ticks: Record<number, string> = {};
163+
if (chapterTime.value) return [];
121164
if (store.curQueueItem?.media_item?.metadata?.chapters) {
122165
store.curQueueItem.media_item.metadata.chapters.forEach((chapter) => {
123166
ticks[chapter.start] = chapter.name;
@@ -141,10 +184,9 @@ const startDragging = function () {
141184
const stopDragging = () => {
142185
isDragging.value = false;
143186
if (!isDragging.value && store.activePlayer) {
144-
api.playerCommandSeek(
145-
store.activePlayer.player_id,
146-
Math.round(tempTime.value),
147-
);
187+
var seekTime = tempTime.value;
188+
if (curChapter.value?.start && chapterTime.value) seekTime = curChapter.value?.start + seekTime;
189+
api.playerCommandSeek(store.activePlayer.player_id, Math.round(seekTime));
148190
}
149191
};
150192

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)