|
1 | | -(function () { |
| 1 | +(function() { |
2 | 2 | 'use strict'; |
3 | 3 |
|
4 | | - function addRandomButton() { |
5 | | - const existingButton = document.querySelector('.random-btn'); |
6 | | - if (existingButton) { |
7 | | - const styles = window.getComputedStyle(existingButton); |
8 | | - return true; |
9 | | - } |
10 | | - |
11 | | - const navContainer = document.querySelector('.navbar-buttons.flex-row.ml-auto.order-xl-2.navbar-nav'); |
12 | | - if (!navContainer) { |
13 | | - return false; |
14 | | - } |
| 4 | + function getIdFromPath(regex) { |
| 5 | + let m = window.location.pathname.match(regex); |
| 6 | + return m ? m[1] : null; |
| 7 | + } |
15 | 8 |
|
16 | | - const randomButtonContainer = document.createElement('div'); |
17 | | - randomButtonContainer.className = 'mr-2'; |
18 | | - randomButtonContainer.innerHTML = ` |
19 | | - <a href="javascript:void(0)"> |
20 | | - <button type="button" class="btn btn-primary random-btn" style="display: inline-block !important; visibility: visible !important;">Random</button> |
21 | | - </a> |
22 | | - `; |
23 | | - randomButtonContainer.querySelector('button').addEventListener('click', loadRandomContent); |
| 9 | + function getPlural(entity) { |
| 10 | + return (entity === "Gallery") ? "Galleries" |
| 11 | + : (entity === "Tag") ? "Tags" |
| 12 | + : (entity === "Image") ? "Images" |
| 13 | + : (entity === "Scene") ? "Scenes" |
| 14 | + : (entity === "Performer") ? "Performers" |
| 15 | + : (entity === "Studio") ? "Studios" |
| 16 | + : (entity === "Group") ? "Groups" |
| 17 | + : entity + "s"; |
| 18 | + } |
24 | 19 |
|
| 20 | + async function randomGlobal(entity, idField, redirectPrefix, internalFilter) { |
| 21 | + const realEntityPlural = getPlural(entity); |
| 22 | + let filter = { per_page: 1 }; |
| 23 | + let variables = { filter }; |
| 24 | + let filterArg = ""; |
| 25 | + let filterVar = ""; |
| 26 | + |
| 27 | + if (internalFilter) { |
| 28 | + filterArg = `, $internal_filter: ${entity}FilterType`; |
| 29 | + filterVar = `, ${entity.toLowerCase()}_filter: $internal_filter`; |
| 30 | + variables.internal_filter = internalFilter; |
| 31 | + } |
25 | 32 |
|
26 | | - if (window.location.pathname.match(/^\/(scenes|images)(?:$|\?)/)) { |
27 | | - let refButton = document.querySelector('a[href="/scenes/new"]'); |
28 | | - if (window.location.pathname.includes('/images')) { |
29 | | - refButton = document.querySelector('a[href="/stats"]'); |
30 | | - } |
31 | | - if (!refButton) { |
32 | | - refButton = navContainer.querySelector('a[href="https://opencollective.com/stashapp"]'); |
| 33 | + const countQuery = ` |
| 34 | + query Find${realEntityPlural}($filter: FindFilterType${filterArg}) { |
| 35 | + find${realEntityPlural}(filter: $filter${filterVar}) { count } |
33 | 36 | } |
34 | | - if (refButton) { |
35 | | - refButton.parentElement.insertAdjacentElement('afterend', randomButtonContainer); |
36 | | - } else { |
37 | | - navContainer.appendChild(randomButtonContainer); |
| 37 | + `; |
| 38 | + let countResp = await fetch('/graphql', { |
| 39 | + method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| 40 | + body: JSON.stringify({ query: countQuery, variables }) |
| 41 | + }); |
| 42 | + let countData = await countResp.json(); |
| 43 | + if (countData.errors) { alert("Error: " + JSON.stringify(countData.errors)); return; } |
| 44 | + const totalCount = countData.data[`find${realEntityPlural}`].count; |
| 45 | + if (!totalCount) { alert("No results found."); return; } |
| 46 | + |
| 47 | + const randomIndex = Math.floor(Math.random() * totalCount); |
| 48 | + let itemVars = { filter: { per_page: 1, page: randomIndex + 1 } }; |
| 49 | + if (internalFilter) itemVars.internal_filter = internalFilter; |
| 50 | + const itemQuery = ` |
| 51 | + query Find${realEntityPlural}($filter: FindFilterType${filterArg}) { |
| 52 | + find${realEntityPlural}(filter: $filter${filterVar}) { ${idField} { id } } |
38 | 53 | } |
39 | | - return true; |
40 | | - } |
| 54 | + `; |
| 55 | + let itemResp = await fetch('/graphql', { |
| 56 | + method: 'POST', headers: { 'Content-Type': 'application/json' }, |
| 57 | + body: JSON.stringify({ query: itemQuery, variables: itemVars }) |
| 58 | + }); |
| 59 | + let itemData = await itemResp.json(); |
| 60 | + if (itemData.errors) { alert("Error: " + JSON.stringify(itemData.errors)); return; } |
| 61 | + const arr = itemData.data[`find${realEntityPlural}`][idField]; |
| 62 | + if (!arr || arr.length === 0) { alert("No results found."); return; } |
| 63 | + window.location.href = `${redirectPrefix}${arr[0].id}`; |
| 64 | + } |
41 | 65 |
|
42 | | - if (window.location.pathname.match(/\/(scenes|images)\/\d+/)) { |
43 | | - const refButton = navContainer.querySelector('a[href="https://opencollective.com/stashapp"]'); |
44 | | - if (refButton) { |
45 | | - refButton.insertAdjacentElement('afterend', randomButtonContainer); |
46 | | - } else { |
47 | | - const firstLink = navContainer.querySelector('a'); |
48 | | - if (firstLink) { |
49 | | - firstLink.parentElement.insertAdjacentElement('afterend', randomButtonContainer); |
50 | | - } else { |
51 | | - navContainer.appendChild(randomButtonContainer); |
52 | | - } |
53 | | - } |
54 | | - return true; |
55 | | - } |
| 66 | + async function randomButtonHandler() { |
| 67 | + const pathname = window.location.pathname.replace(/\/$/, ''); |
56 | 68 |
|
57 | | - return false; |
58 | | - } |
| 69 | + if (pathname === '/scenes') return randomGlobal('Scene', 'scenes', '/scenes/'); |
| 70 | + if (pathname === '/performers') return randomGlobal('Performer', 'performers', '/performers/'); |
| 71 | + if (pathname === '/groups') return randomGlobal('Group', 'groups', '/groups/'); |
| 72 | + if (pathname === '/studios') return randomGlobal('Studio', 'studios', '/studios/'); |
| 73 | + if (pathname === '/tags') return randomGlobal('Tag', 'tags', '/tags/'); |
| 74 | + if (pathname === '/galleries') return randomGlobal('Gallery', 'galleries', '/galleries/'); |
| 75 | + if (pathname === '/images') return randomGlobal('Image', 'images', '/images/'); |
59 | 76 |
|
60 | | - function getParentHierarchy(element) { |
61 | | - const hierarchy = []; |
62 | | - let current = element; |
63 | | - while (current && current !== document.body) { |
64 | | - hierarchy.push(current.tagName + (current.className ? '.' + current.className.split(' ').join('.') : '')); |
65 | | - current = current.parentElement; |
66 | | - } |
67 | | - return hierarchy.join(' > '); |
68 | | - } |
| 77 | + // --- INTERN --- |
| 78 | + let studioId = getIdFromPath(/^\/studios\/(\d+)\/scenes/); |
| 79 | + if (studioId) return randomGlobal('Scene', 'scenes', '/scenes/', { studios: { value: [studioId], modifier: "INCLUDES_ALL" } }); |
69 | 80 |
|
70 | | - async function loadRandomContent() { |
71 | | - try { |
72 | | - const isScenes = window.location.pathname.includes('/scenes'); |
73 | | - const isImages = window.location.pathname.includes('/images'); |
74 | | - const type = isScenes ? 'scenes' : isImages ? 'images' : 'scenes'; |
75 | | - |
76 | | - const countQuery = ` |
77 | | - query Find${type.charAt(0).toUpperCase() + type.slice(1)}($filter: FindFilterType) { |
78 | | - find${type.charAt(0).toUpperCase() + type.slice(1)}(filter: $filter) { |
79 | | - count |
80 | | - } |
81 | | - } |
82 | | - `; |
83 | | - const countVariables = { filter: { per_page: 1 } }; |
84 | | - |
85 | | - const countResponse = await fetch('/graphql', { |
86 | | - method: 'POST', |
87 | | - headers: { 'Content-Type': 'application/json' }, |
88 | | - body: JSON.stringify({ query: countQuery, variables: countVariables }) |
89 | | - }); |
90 | | - |
91 | | - const countResult = await countResponse.json(); |
92 | | - if (countResult.errors) { |
93 | | - return; |
94 | | - } |
| 81 | + let groupId = getIdFromPath(/^\/groups\/(\d+)\/scenes/); |
| 82 | + if (groupId) return randomGlobal('Scene', 'scenes', '/scenes/', { groups: { value: [groupId], modifier: "INCLUDES_ALL" } }); |
95 | 83 |
|
96 | | - const totalCount = countResult.data[`find${type.charAt(0).toUpperCase() + type.slice(1)}`].count; |
97 | | - if (totalCount === 0) { |
98 | | - return; |
99 | | - } |
| 84 | + let performerId = getIdFromPath(/^\/performers\/(\d+)\/scenes/); |
| 85 | + if (performerId) return randomGlobal('Scene', 'scenes', '/scenes/', { performers: { value: [performerId], modifier: "INCLUDES_ALL" } }); |
100 | 86 |
|
101 | | - const randomIndex = Math.floor(Math.random() * totalCount); |
102 | | - const itemQuery = ` |
103 | | - query Find${type.charAt(0).toUpperCase() + type.slice(1)}($filter: FindFilterType) { |
104 | | - find${type.charAt(0).toUpperCase() + type.slice(1)}(filter: $filter) { |
105 | | - ${type} { |
106 | | - id |
107 | | - } |
108 | | - } |
109 | | - } |
110 | | - `; |
111 | | - const itemVariables = { |
112 | | - filter: { per_page: 1, page: Math.floor(randomIndex / 1) + 1 } |
113 | | - }; |
114 | | - |
115 | | - const itemResponse = await fetch('/graphql', { |
116 | | - method: 'POST', |
117 | | - headers: { 'Content-Type': 'application/json' }, |
118 | | - body: JSON.stringify({ query: itemQuery, variables: itemVariables }) |
119 | | - }); |
120 | | - |
121 | | - const itemResult = await itemResponse.json(); |
122 | | - if (itemResult.errors) { |
123 | | - return; |
124 | | - } |
| 87 | + let tagId = getIdFromPath(/^\/tags\/(\d+)\/scenes/); |
| 88 | + if (tagId) return randomGlobal('Scene', 'scenes', '/scenes/', { tags: { value: [tagId], modifier: "INCLUDES_ALL" } }); |
125 | 89 |
|
126 | | - const items = itemResult.data[`find${type.charAt(0).toUpperCase() + type.slice(1)}`][type]; |
127 | | - if (items.length === 0) { |
128 | | - return; |
129 | | - } |
| 90 | + let galleryId = getIdFromPath(/^\/galleries\/(\d+)/); |
| 91 | + if (galleryId) return randomGlobal('Image', 'images', '/images/', { galleries: { value: [galleryId], modifier: "INCLUDES_ALL" } }); |
130 | 92 |
|
131 | | - const itemId = items[0].id; |
132 | | - window.location.href = `/${type}/${itemId}`; |
133 | | - } catch (error) { |
134 | | - console.error(error); |
135 | | - } |
| 93 | + alert('Not supported'); |
136 | 94 | } |
137 | 95 |
|
138 | | - window.addEventListener('load', () => { |
139 | | - addRandomButton(); |
140 | | - }); |
| 96 | + function addRandomButton() { |
| 97 | + if (document.querySelector('.random-btn')) return; |
| 98 | + const navContainer = document.querySelector('.navbar-buttons.flex-row.ml-auto.order-xl-2.navbar-nav'); |
| 99 | + if (!navContainer) return; |
141 | 100 |
|
142 | | - document.addEventListener('click', (event) => { |
143 | | - const target = event.target.closest('a'); |
144 | | - if (target && target.href) { |
145 | | - setTimeout(() => { |
146 | | - addRandomButton(); |
147 | | - }, 1500); |
148 | | - } |
149 | | - }); |
| 101 | + const randomButtonContainer = document.createElement('div'); |
| 102 | + randomButtonContainer.className = 'mr-2'; |
| 103 | + randomButtonContainer.innerHTML = ` |
| 104 | + <a href="javascript:void(0)"> |
| 105 | + <button type="button" class="btn btn-primary random-btn" style="display: inline-block !important; visibility: visible !important;">Random</button> |
| 106 | + </a> |
| 107 | + `; |
| 108 | + randomButtonContainer.querySelector('button').addEventListener('click', randomButtonHandler); |
150 | 109 |
|
151 | | - window.addEventListener('popstate', () => { |
152 | | - setTimeout(() => { |
153 | | - addRandomButton(); |
154 | | - }, 1500); |
155 | | - }); |
| 110 | + navContainer.appendChild(randomButtonContainer); |
| 111 | + } |
156 | 112 |
|
157 | | - window.addEventListener('hashchange', () => { |
158 | | - setTimeout(() => { |
159 | | - addRandomButton(); |
160 | | - }, 1500); |
| 113 | + window.addEventListener('load', () => addRandomButton()); |
| 114 | + document.addEventListener('click', (event) => { |
| 115 | + const target = event.target.closest('a'); |
| 116 | + if (target && target.href) setTimeout(() => addRandomButton(), 1500); |
161 | 117 | }); |
162 | | - |
| 118 | + window.addEventListener('popstate', () => setTimeout(() => addRandomButton(), 1500)); |
| 119 | + window.addEventListener('hashchange', () => setTimeout(() => addRandomButton(), 1500)); |
163 | 120 | const navContainer = document.querySelector('.navbar-buttons.flex-row.ml-auto.order-xl-2.navbar-nav'); |
164 | 121 | if (navContainer) { |
165 | | - const observer = new MutationObserver((mutations) => { |
166 | | - mutations.forEach(m => { |
167 | | - }); |
168 | | - if (!document.querySelector('.random-btn')) { |
169 | | - addRandomButton(); |
170 | | - } |
| 122 | + const observer = new MutationObserver(() => { |
| 123 | + if (!document.querySelector('.random-btn')) addRandomButton(); |
171 | 124 | }); |
172 | 125 | observer.observe(navContainer, { childList: true, subtree: true }); |
173 | | - } else { |
174 | 126 | } |
175 | | - |
176 | | - |
177 | 127 | let intervalAttempts = 0; |
178 | 128 | setInterval(() => { |
179 | 129 | intervalAttempts++; |
180 | 130 | addRandomButton(); |
181 | 131 | }, intervalAttempts < 60 ? 500 : 2000); |
| 132 | + |
182 | 133 | })(); |
0 commit comments