Skip to content

Commit 27df1b9

Browse files
committed
Implement chapter time over full audiobook duration
1 parent b3e46e9 commit 27df1b9

File tree

3 files changed

+73
-19
lines changed

3 files changed

+73
-19
lines changed

src/layouts/default/PlayerOSD/PlayerTimeline.vue

Lines changed: 57 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,75 @@ 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+
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+
return curChapter.value?.end! - curChapter.value?.start!;
116+
}
117+
return store.curQueueItem?.duration;
118+
});
119+
120+
const curQueueItemTime = computed(() => {
121+
if (isDragging.value) {
122+
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
123+
tempTime.value = curTimeValue.value;
124+
return curTimeValue.value;
125+
}
126+
127+
if (store.activePlayerQueue) {
128+
if (
129+
chapterTime.value &&
130+
store.curQueueItem?.media_item?.media_type == MediaType.AUDIOBOOK
131+
) {
132+
return store.activePlayerQueue?.elapsed_time - curChapter.value?.start!;
133+
}
134+
return store.activePlayerQueue.elapsed_time;
135+
}
136+
return 0;
137+
});
138+
92139
const playerCurTimeStr = computed(() => {
93-
if (!store.curQueueItem) return "0:00";
140+
if (!playerCurQueueItemDuration.value || !curQueueItemTime.value) return "0:00";
94141
if (showRemainingTime.value) {
95142
return `-${formatDuration(
96-
store.curQueueItem.duration - curQueueItemTime.value,
143+
playerCurQueueItemDuration.value - curQueueItemTime.value,
97144
)}`;
98145
} else {
99146
return `${formatDuration(curQueueItemTime.value)}`;
100147
}
101148
});
149+
102150
const playerTotalTimeStr = computed(() => {
103-
if (!store.curQueueItem) return "";
104-
if (!store.curQueueItem.duration) return "";
151+
if (!playerCurQueueItemDuration.value || !store.curQueueItem) return "";
105152
if (store.curQueueItem.media_item?.media_type == MediaType.RADIO) return "";
106-
const totalSecs = store.curQueueItem.duration;
153+
const totalSecs = playerCurQueueItemDuration.value;
107154
return formatDuration(totalSecs);
108155
});
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-
});
118156
119157
const chapterTicks = computed(() => {
120158
const ticks: Record<number, string> = {};
159+
if (chapterTime.value) return [];
121160
if (store.curQueueItem?.media_item?.metadata?.chapters) {
122161
store.curQueueItem.media_item.metadata.chapters.forEach((chapter) => {
123162
ticks[chapter.start] = chapter.name;
@@ -141,10 +180,9 @@ const startDragging = function () {
141180
const stopDragging = () => {
142181
isDragging.value = false;
143182
if (!isDragging.value && store.activePlayer) {
144-
api.playerCommandSeek(
145-
store.activePlayer.player_id,
146-
Math.round(tempTime.value),
147-
);
183+
var seekTime = tempTime.value;
184+
if (chapterTime.value) seekTime = curChapter.value?.start! + seekTime;
185+
api.playerCommandSeek(store.activePlayer.player_id, Math.round(seekTime));
148186
}
149187
};
150188

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)