Skip to content

Commit f82757d

Browse files
authored
Audio Player plugin (#546)
1 parent 76629fc commit f82757d

File tree

6 files changed

+250
-0
lines changed

6 files changed

+250
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* Stash Audio Player */
2+
.VideoPlayer.audio .scrubber-wrapper,
3+
.VideoPlayer.audio .vjs-big-play-button,
4+
.VideoPlayer.audio .vjs-big-button-group,
5+
.VideoPlayer.audio .vjs-loading-spinner,
6+
.VideoPlayer.audio .vjs-skip-button {
7+
display: none !important;
8+
}
9+
.VideoPlayer.audio .vjs-control-bar {
10+
display: flex !important;
11+
opacity: 100 !important;
12+
}
13+
.VideoPlayer.audio .vjs-seek-button {
14+
display: block !important;
15+
}
16+
.VideoPlayer.audio .video-js {
17+
background-color: #202b33 !important;
18+
width: 100%;
19+
}
20+
.VideoPlayer.audio #VideoJsPlayer_html5_api {
21+
display: none !important;
22+
}
23+
.VideoPlayer.audio .video-wrapper {
24+
height: 7rem !important;
25+
overflow: visible;
26+
}
27+
.VideoPlayer.audio > .vjs-poster {
28+
height: 56.25vw !important;
29+
position: relative !important;
30+
}
31+
#audioToggle.enabled {
32+
color: #48aff0;
33+
}
34+
#audioToggle:hover {
35+
color: #fff;
36+
}
37+
#audioToggle {
38+
color: rgba(191,204,214,.5);
39+
}

plugins/AudioPlayer/AudioPlayer.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Stash Audio Player
2+
(async function () {
3+
"use strict"
4+
5+
const { waitForElement, PathElementListener, getConfiguration, callGQL, baseURL } = window.csLib
6+
7+
const defaultConfig = { audioExtensions: "mp3, m4a", useTag: false }
8+
const config = await getConfiguration("AudioPlayer", defaultConfig)
9+
const pluginConfig = {
10+
...defaultConfig,
11+
...config,
12+
}
13+
14+
function setAudioElement(bool) {
15+
const pl = document.querySelector(".VideoPlayer")
16+
var elm = document.getElementById("VideoJsPlayer_html5_api")
17+
const sourceElements = Array.from(
18+
document.querySelectorAll(
19+
"#VideoJsPlayer > div.vjs-control-bar > div.vjs-source-selector.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button > div > ul > li > span.vjs-menu-item-text"
20+
)
21+
)
22+
const hlsButton = sourceElements.find((el) => el.textContent.trim().toLowerCase() === "hls")
23+
if (bool) {
24+
pl.classList.add("audio")
25+
document.querySelector(".VideoPlayer .video-wrapper").before(document.querySelector(".vjs-poster"))
26+
if (hlsButton) {
27+
hlsButton.click()
28+
}
29+
} else {
30+
document.getElementById("VideoJsPlayer_html5_api").after(document.querySelector(".vjs-poster"))
31+
pl.classList.remove("audio")
32+
sourceElements[0].click()
33+
}
34+
}
35+
36+
function audiotoggle() {
37+
const elm = document.querySelector(".scene-toolbar-group")
38+
const btng = document.createElement("div")
39+
const btn = document.createElement("button")
40+
const pl = document.querySelector(".VideoPlayer")
41+
if (pl.classList.contains("audio")) {
42+
btn.classList.add("enabled")
43+
}
44+
btn.id = "audioToggle"
45+
btn.classList.add("minimal", "btn", "btn-secondary")
46+
//btn.innerHTML = "Audio Only";
47+
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2.25a.75.75 0 0 0-.75.75v11.26a4.25 4.25 0 1 0 1.486 2.888A1 1 0 0 0 12.75 17V7.75H18a2.75 2.75 0 1 0 0-5.5z"/></svg>`
48+
btn.onclick = function () {
49+
const pl = document.querySelector(".VideoPlayer")
50+
const bt = document.getElementById("audioToggle")
51+
if (pl.classList.contains("audio")) {
52+
setAudioElement(false)
53+
bt.classList.remove("enabled")
54+
} else {
55+
setAudioElement(true)
56+
bt.classList.add("enabled")
57+
}
58+
}
59+
btng.classList.add("btn-group")
60+
btng.appendChild(btn)
61+
elm.appendChild(btng)
62+
}
63+
64+
async function getScene() {
65+
const currentPath = window.location.pathname
66+
const idRegExp = /\/scenes\/(\d+).*/
67+
const id = idRegExp.exec(currentPath)[1]
68+
const query = `query FindScene($id: ID!, $checksum: String) {findScene(id: $id, checksum: $checksum) { id\n files { id\n path\n __typename }\n tags { id\n name\n __typename }\n __typename}}`
69+
const variables = { id }
70+
const response = await callGQL({ query, variables })
71+
return response
72+
}
73+
74+
function findAudioTag(sceneInfo) {
75+
const tags = sceneInfo["findScene"]["tags"]
76+
const audioTag = tags.find((tag) => tag.name === "Audio")
77+
if (audioTag) {
78+
setAudioElement(true)
79+
}
80+
// waitForElement(".tag-item", function (){
81+
// const links = document.querySelectorAll('.tag-item a');
82+
// if (links) {
83+
// links.forEach(link => {
84+
// const divText = link.querySelector('div').textContent.trim();
85+
// if (divText === "Audio") {
86+
// setAudioElement(true);
87+
// }
88+
// });
89+
// }})
90+
}
91+
92+
function findAudioExtension(sceneInfo) {
93+
const filesExt = sceneInfo["findScene"]["files"].map((file) => {
94+
const fileNameArray = file.path.split(".")
95+
return fileNameArray[fileNameArray.length - 1]
96+
})
97+
const extToSearch = pluginConfig["audioExtensions"].split(",").map((ext) => ext.trim())
98+
const audioFiles = filesExt.find((ext) => extToSearch.includes(ext))
99+
if (audioFiles) {
100+
setAudioElement(true)
101+
}
102+
}
103+
104+
PathElementListener(
105+
baseURL + "scenes/",
106+
"#VideoJsPlayer > div.vjs-control-bar > div.vjs-source-selector.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button > div > ul > li > span.vjs-menu-item-text",
107+
async function () {
108+
const sceneInfo = await getScene()
109+
const useTag = pluginConfig["useTag"]
110+
if (useTag) {
111+
findAudioTag(sceneInfo)
112+
} else {
113+
findAudioExtension(sceneInfo)
114+
}
115+
waitForElement(".scene-toolbar-group", audiotoggle)
116+
}
117+
)
118+
})()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: AudioPlayer
2+
description: Add an audio-only toggle button and support for audio files in the Stash player through hls transcoder.
3+
version: 0.3
4+
ui:
5+
requires:
6+
- CommunityScriptsUILibrary
7+
javascript:
8+
- AudioPlayer.js
9+
css:
10+
- AudioPlayer.css
11+
12+
settings:
13+
audioExtensions:
14+
displayName: Audio Extensions
15+
description: "Extensions to mach as audio files. Default: mp3, m4a"
16+
type: STRING
17+
useTag:
18+
displayName: Use Tag
19+
description: Use "Audio" tag instead of extensions.
20+
type: BOOLEAN
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Stash Audio Player Lite */
2+
.VideoPlayer.audio .scrubber-wrapper,
3+
.VideoPlayer.audio .vjs-big-play-button,
4+
.VideoPlayer.audio .vjs-big-button-group,
5+
.VideoPlayer.audio .vjs-loading-spinner,
6+
.VideoPlayer.audio .vjs-skip-button {
7+
display: none !important;
8+
}
9+
.VideoPlayer.audio .vjs-control-bar {
10+
display: flex !important;
11+
opacity: 100 !important;
12+
}
13+
.VideoPlayer.audio .vjs-seek-button {
14+
display: block !important;
15+
}
16+
.VideoPlayer.audio .video-js {
17+
background-color: #202b33 !important;
18+
width: 100%;
19+
}
20+
.VideoPlayer.audio #VideoJsPlayer_html5_api {
21+
display: none !important;
22+
}
23+
.VideoPlayer.audio .video-wrapper {
24+
height: 7rem !important;
25+
overflow: visible;
26+
}
27+
.VideoPlayer.audio > .vjs-poster {
28+
height: 56.25vw !important;
29+
position: relative !important;
30+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
(function () {
2+
"use strict";
3+
const PluginApi = window.PluginApi;
4+
const React = PluginApi.React;
5+
6+
function setStyle() {
7+
if(!document.querySelector(".VideoPlayer .video-wrapper") || !document.querySelector(".vjs-poster")) {
8+
window.setTimeout(setStyle, 100)
9+
} else {
10+
document.querySelector(".VideoPlayer .video-wrapper").before(document.querySelector(".vjs-poster"))
11+
document.querySelector(".VideoPlayer").classList.add("audio")
12+
}
13+
}
14+
15+
PluginApi.Event.addEventListener("stash:location", async (e) => {
16+
const path = e.detail.data.location.pathname;
17+
const idRegExp = /.*\/scenes\/(\d+)/;
18+
if (idRegExp.test(path)) {
19+
await PluginApi.utils.loadComponents([
20+
PluginApi.loadableComponents.ScenePlayer
21+
]);
22+
PluginApi.patch.instead("ScenePlayer", function (props, _, originalComponent) {
23+
const file = props.scene.files[0];
24+
let scene = props.scene;
25+
if (file.video_codec === "") {
26+
scene = { ...scene,
27+
sceneStreams: props.scene.sceneStreams.filter((ss) => ss.label.toUpperCase() === 'HSL')
28+
};
29+
poster()
30+
}
31+
return originalComponent({ ...props, scene });
32+
});
33+
}
34+
});
35+
})();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: AudioPlayerLite
2+
description: This plugin identifies files with no video codec and plays them as audio.
3+
version: 0.1
4+
ui:
5+
javascript:
6+
- AudioPlayerLite.js
7+
css:
8+
- AudioPlayerLite.css

0 commit comments

Comments
 (0)