This commit is contained in:
2025-09-18 15:31:27 +02:00
commit 2abba0c2b7
406 changed files with 31879 additions and 0 deletions

125
resources/css/app.css Normal file
View File

@@ -0,0 +1,125 @@
@import "@fortawesome/fontawesome-free/css/all.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
@theme {
--breakpoint-xs: 30rem;
}
/* Player */
.plyr--full-ui input[type="range"] {
color: var(--plyr-range-fill-background, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
}
.plyr__control--overlaid {
background: var(--plyr-video-control-background-hover, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
}
.plyr--video .plyr__control.plyr__tab-focus,
.plyr--video .plyr__control:hover,
.plyr--video .plyr__control[aria-expanded="true"] {
background: var(--plyr-video-control-background-hover, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
}
.plyr__menu__container .plyr__control[role="menuitemradio"][aria-checked="true"]::before {
background: var(--plyr-control-toggle-checked-background, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
}
.plyr--full-ui {
border-radius: 15px;
}
/* Player Ambient */
.decoy {
position: absolute;
filter: blur(70px);
-webkit-filter: blur(70px);
z-index: 0;
}
/* Homepage Branding Fade */
.fade-img {
-webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0))
}
/* Theme Switcher Button */
input:checked~.dot {
transform: translateX(100%);
}
#plyr__time_skip {
background: #c61e54;
border: 0;
border-radius: 50%;
color: #fff;
left: 50%;
min-width: 60px;
width: min-content;
max-width: 100px;
max-height: 90px;
opacity: 0;
display: table-cell;
text-align: center;
vertical-align: middle;
transform: translate(-50%, -50%);
padding-top: 15px;
padding-bottom: 15px;
position: absolute;
top: 50%;
transition: 1s;
z-index: 3;
pointer-events: none;
box-shadow: 0px 0px 45px #000000;
}
/* DL Button Glow */
.hover\:glow:hover {
filter: drop-shadow(0px 0px 7px rgba(255, 29, 72, 0.5));
}
/* latin */
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-400-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-400-normal.woff) format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-400-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-400-normal.woff) format('woff');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-600-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-600-normal.woff) format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff) format('woff');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

View File

@@ -0,0 +1,52 @@
import Tagify from '@yaireo/tagify';
import '@yaireo/tagify/dist/tagify.css';
const taginput = document.querySelector("#tags");
const studioinput = document.querySelector("#studio");
const episode_id = document.getElementById('e_id').value;
// Get Tags from API
window.axios.get('/admin/tags/' + episode_id).then(function (response) {
if (response.status != 200) {
return;
}
var tagify = new Tagify(taginput, {
whitelist: response.data.tags,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true
}
});
tagify.addTags(response.data.episodetags);
}).catch(function (error) {
console.log(error);
});
// Get Studio from API
window.axios.get('/admin/studio/' + episode_id).then(function (response) {
if (response.status != 200) {
return;
}
var tagify = new Tagify(studioinput, {
whitelist: response.data.studios,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true
}
});
tagify.addTags(response.data.episodestudios);
}).catch(function (error) {
console.log(error);
});

View File

@@ -0,0 +1,29 @@
import Tagify from '@yaireo/tagify';
import '@yaireo/tagify/dist/tagify.css';
const taginput = document.querySelector("#subtitles");
const episode_id = document.getElementById('e_id').value;
// Get Tags from API
window.axios.get('/admin/subtitles/' + episode_id).then(function (response) {
if (response.status != 200) {
return;
}
var tagify = new Tagify(taginput, {
whitelist: response.data.subs,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true
}
});
tagify.addTags(response.data.episodesubs);
}).catch(function (error) {
console.log(error);
});

20
resources/js/app.js Normal file
View File

@@ -0,0 +1,20 @@
import './bootstrap';
// import { Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm';
// Alpine.start();
import {
Collapse,
Carousel,
Clipboard,
Modal,
Lightbox,
Tooltip,
Tab,
Ripple,
initTE,
} from "tw-elements";
initTE({ Collapse, Carousel, Clipboard, Modal, Tab, Lightbox, Tooltip, Ripple });
import 'hammerjs';

32
resources/js/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,32 @@
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// import Pusher from 'pusher-js';
// window.Pusher = Pusher;
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
// enabledTransports: ['ws', 'wss'],
// });

View File

@@ -0,0 +1,12 @@
export function isIOS() {
return [
'iPad Simulator',
'iPhone Simulator',
'iPod Simulator',
'iPad',
'iPhone',
'iPod'
].includes(navigator.platform)
// iPad on iOS 13 detection
|| (navigator.userAgent.includes("Mac") && "ontouchend" in document)
}

View File

@@ -0,0 +1,59 @@
if (document.getElementById("playlist-add")) {
function createPlaylist() {
console.log('Adding to Playlist: ' + document.querySelector("#playlist").value)
window.axios.post('/hentai/add-to-playlist', {
playlist: document.getElementById('playlist').value,
episode_id: document.getElementById('e_id').value
}).then(function (response) {
if (response.status == 200) {
document.getElementById("playlist-cancel").click();
if (response.data.message == 'already-added') {
Swal.fire({
title: "Already added!",
text: "Episode was already added to that Playlist!",
icon: "warning"
});
}
if (response.data.message == 'success') {
Swal.fire({
title: "Success!",
text: "Added episode to the playlist!",
icon: "success"
});
}
}
}).catch(function (error) {
console.log(error);
});
}
document.querySelector("#playlist-add").addEventListener("click", createPlaylist);
}
if (document.getElementById("playlist-create-and-add")) {
function createAndAddPlaylist() {
window.axios.post('/hentai/create-playlist', {
name: document.getElementById('name').value,
visiblity: document.getElementById('visiblity').value
}).then(function (response) {
window.axios.post('/hentai/add-to-playlist', {
playlist: response.data.playlist_id,
episode_id: document.getElementById('e_id').value
}).then(function (response) {
if (response.status == 200) {
document.getElementById("playlist-cancel").click();
}
}).catch(function (error) {
console.log(error);
});
}).catch(function (error) {
console.log(error);
});
}
document.querySelector("#playlist-create-and-add").addEventListener("click", createAndAddPlaylist);
}

View File

@@ -0,0 +1,94 @@
export function addVideoTracks(streamServer, apiResponse, av1Supported, dashSupported) {
if (dashSupported) {
return addDashTracks(streamServer, apiResponse, av1Supported);
}
return addLegacyTracks(streamServer, apiResponse, av1Supported);
}
function addDashTracks(streamServer, apiResponse, av1Supported) {
var data = [];
// 720p
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/720/manifest.mpd',
size: 720,
mode: 'mpd',
});
if (av1Supported) {
// 1080p
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/1080/manifest.mpd',
size: 1080,
mode: 'mpd',
});
// 2160p
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/2160/manifest.mpd',
size: 2160,
mode: 'mpd',
});
if (apiResponse.interpolated == 1) {
// 1080p Interpolated
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/1080i/manifest.mpd',
size: 1081,
mode: 'mpd',
});
}
if (apiResponse.interpolated_uhd == 1) {
// 2160p Interpolated
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/2160i/manifest.mpd',
size: 2161,
mode: 'mpd',
});
}
}
return data;
}
function addLegacyTracks(streamServer, apiResponse, av1Supported) {
var data = [];
// 720p
data.push({
src: streamServer + '/' + apiResponse.stream_url + '/x264.720p.mp4',
type: 'video/mp4',
size: 720,
});
return data;
}
export function addSubtitleTracks(streamServer, apiResponse) {
var data = [];
// Default
data.push({
kind: 'captions',
label: 'English',
srclang: 'en',
src: '',
default: true,
});
for (var key in apiResponse.extra_subtitles) {
data.push({
kind: 'captions',
label: apiResponse.extra_subtitles[key] + ' (Auto Transl.)',
srclang: key,
src: '',
default: false,
});
}
return data;
}

View File

@@ -0,0 +1,144 @@
export function initMobileWidescreen() {
// Mobile widescreen button
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
// Add Button to fit screen
var controls = document.getElementsByClassName('plyr__controls')[0];
var newButton = `
<button class="plyr__controls__item plyr__control" id="mobile-screen" type="button" aria-pressed="false" style="padding: 5px; padding-top: 2px; padding-bottom: 2px;">
<i class="fa-solid fa-arrows-left-right-to-line" ></i>
</button>
`;
controls.insertAdjacentHTML('beforeend', newButton);
// Add event listener
var stateButton = true;
var videoTemp = document.querySelector('video');
const mobilebutton = document.getElementById('mobile-screen');
mobilebutton.addEventListener('click', function() {
if (! stateButton) {
videoTemp.style.objectFit = 'cover';
stateButton = true;
} else {
videoTemp.style.objectFit = null;
stateButton = false;
}
});
// Set default
videoTemp.style.objectFit = 'cover';
}
}
export function mobileDoubleClick(player) {
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
const byClass = document.getElementsByClassName.bind(document),
createElement = document.createElement.bind(document);
// Remove all dblclick stuffs
player.eventListeners.forEach(function (eventListener) {
if (eventListener.type === 'dblclick') {
eventListener.element.removeEventListener(eventListener.type, eventListener.callback, eventListener.options);
}
});
// Create overlay that will show the skipped time
const skip_ol = createElement("div");
skip_ol.id = "plyr__time_skip"
byClass("plyr")[0].appendChild(skip_ol)
// A class to manage multi click count and remember last clicked side (may cause issue otherwise)
class multiclick_counter {
constructor() {
this.timers = []; // collection of timers. Important
this.count = 0; // click count
this.reseted = 0; // before resetting what was the count
this.last_side = null; // L C R 3sides
}
clicked() {
this.count += 1
var xcount = this.count; // will be checked if click count increased in the time
this.timers.push(setTimeout(this.reset.bind(this, xcount), 500)); // wait till 500ms for next click
return this.count
}
reset_count(n) {
// Reset count if clicked on the different side
this.reseted = this.count
this.count = n
for (var i = 0; i < this.timers.length; i++) {
clearTimeout(this.timers[i]);
}
this.timer = []
}
reset(xcount) {
if (this.count > xcount) { return } // return if clicked after timer started
// Reset otherwise
this.count = 0;
this.last_side = null;
this.reseted = 0;
skip_ol.style.opacity = "0";
this.timer = []
}
}
var counter = new multiclick_counter();
const poster = byClass("plyr__poster")[0]
poster.onclick = function (e) {
const count = counter.clicked()
if (count < 2) { return } // if not double click
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left; //x position within the element.
// The relative position of click on video
const width = e.target.offsetWidth;
const perc = x * 100 / width;
var panic = true; // panic if the side needs to be checked
var last_click = counter.last_side
if (last_click == null) {
panic = false
}
if (perc < 40) {
if (player.currentTime == 0) {
return // won't seek beyond 0
}
counter.last_side = "L"
if (panic && last_click != "L") {
counter.reset_count(1)
return
}
skip_ol.style.opacity = "0.9";
player.rewind()
skip_ol.innerHTML = "<i class=\"fa-solid fa-backward\"></i> " + ((count - 1) * 10) + "s";
}
else if (perc > 60) {
if (player.currentTime == player.duration) {
return // won't seek beyond duration
}
counter.last_side = "R"
if (panic && last_click != "R") {
counter.reset_count(1)
return
}
skip_ol.style.opacity = "0.9";
last_click = "R"
player.forward()
skip_ol.innerHTML = "<i class=\"fa-solid fa-forward\"></i> " + ((count - 1) * 10) + "s";
}
else {
player.togglePlay()
counter.last_click = "C"
}
}
}
}

View File

@@ -0,0 +1,45 @@
export function serverSelectMenuItem(selectedIndex) {
return `
<button id="server-select" data-plyr="settings" type="button" class="plyr__control plyr__control--forward" role="menuitem" aria-haspopup="true">
<span>Server<span class="plyr__menu__value">CDN` + (selectedIndex + 1) + `</span></span>
</button>`;
}
export function serverSelectSubmenu(selectedIndex, serverCount) {
let htmlList = `
<div id="server-select-list" hidden>
<button type="button" class="plyr__control plyr__control--back" id="server-select-list-back-btn">
<span aria-hidden="true">Server</span><span class="plyr__sr-only">Go back to previous menu</span>
</button>
<div role="menu">
`;
for (let i = 0; i < serverCount; i++) {
let checked = selectedIndex == i ? 'true' : 'false';
let index = i + 1;
htmlList += `
<button data-plyr="server" type="button" role="menuitemradio" class="plyr__control change_server" aria-checked="` + checked + `" value="` + i +`">
<span>Server ` + index + `<span class="plyr__menu__value"><span class="plyr__badge">CDN` + index + `</span></span></span>
</button>
`;
}
htmlList += `
</div>
</div>
`;
return htmlList;
}
export function serverSelectMenuClickToggle() {
if (document.getElementById('server-select-list').hidden) {
document.querySelector('div[role="menu"]').hidden = true;
document.getElementById('server-select-list').hidden = false;
return;
}
document.getElementById('server-select-list').hidden = true;
document.querySelector('div[role="menu"]').hidden = false;
}

462
resources/js/player.js Normal file
View File

@@ -0,0 +1,462 @@
// Plyr Player
import Plyr from 'plyr/dist/plyr.polyfilled.min.js';
import 'plyr/dist/plyr.css';
// Vidstack Player
import 'vidstack/player/styles/default/theme.css';
import 'vidstack/player/styles/default/layouts/video.css';
import { VidstackPlayer, VidstackPlayerLayout } from 'vidstack/global/player';
// Dash Support
import dashjs from 'dashjs';
// Subtitle Support
import SubtitlesOctopus from '@jellyfin/libass-wasm';
// Custom JS
import { initMobileWidescreen } from './player-mobile';
import { mobileDoubleClick } from './player-mobile'
import { playNextPlaylistVideo } from './playlist';
import { addVideoTracks } from './player-data';
import { addSubtitleTracks } from './player-data';
import { serverSelectMenuItem, serverSelectSubmenu, serverSelectMenuClickToggle } from './player-server-select';
import { isIOS } from './detect-ios';
// Variables
var player = null;
var av1Supported = (!!document.createElement('video').canPlayType('video/webm; codecs="av01.0.05M.08, opus"'));
var dashSupported = dashjs.supportsMediaSource();
var apiResponse = {};
var volume = 0.5;
var captions = true;
var lastTime = 0.0;
var streamServer = '';
var streamServers = [];
var streamServerIndex = 0;
var streamServerCount = 0;
var ambientMode = true;
var serverFallback = false;
var saveInterval;
var subtitleInstance = null;
var controls = [
'play-large', // The large play button in the center
'play', // Play/pause playback
'progress', // The progress bar and scrubber for playback and buffering
'current-time', // The current time of playback
'duration', // The full duration of the media
'mute', // Toggle mute
'volume', // Volume control
'captions', // Toggle captions
'settings', // Settings menu
'fullscreen', // Toggle fullscreen
];
// Load Volume from LocalStorage
if (localStorage.hstreamVolume) {
volume = parseFloat(localStorage.getItem('hstreamVolume')).toFixed(2);
console.log('Loaded Audio Volume from Local Storage: ' + volume);
}
// Load Captions from LocalStorage
if (localStorage.hstreamCaptions) {
captions = (localStorage.getItem('hstreamCaptions') == 'true');
console.log('Loaded Captions Status from Local Storage: ' + captions);
}
// Asia Server Fallback
if (localStorage.hstreamServerFallback) {
serverFallback = (localStorage.getItem('hstreamServerFallback') == 'true');
console.log('Loaded Captions Status from Local Storage: ' + captions);
}
// Alert User when AV1 is not supported
if (!av1Supported) {
document.getElementById("av1-unsupported").classList.remove("hidden");
}
function initDash(data, player) {
const video = document.querySelector('video');
data.forEach(function (el) {
if (el.mode === 'mpd' && el.size === player.config.quality.selected) {
const dash = dashjs.MediaPlayer().create();
dash.initialize(video, el.src, true);
// Expose player and dash so they can be used from the console
window.player = player;
window.dash = dash;
}
});
}
function setCanvasDimension(canvas, video) {
canvas.height = video.offsetHeight;
canvas.width = video.offsetWidth;
}
function paintStaticVideo(ctx, video) {
if (localStorage.theme == 'light') {
return;
}
if (!ambientMode) {
return;
}
ctx.drawImage(video, 0, 0, video.offsetWidth, video.offsetHeight);
}
function toggleAmbientMode() {
let canvas = document.getElementById("ambientVideo"), ctx = canvas.getContext("2d"), video = document.getElementsByTagName('video')[0];
if (ambientMode) {
ambientMode = false;
localStorage.ambientMode = 'false';
setCanvasDimension(canvas, video);
document.getElementById('ambient-mode-toggle').innerHTML = '<span>Ambient Mode<span class="plyr__menu__value">Off</span></span>';
} else {
ambientMode = true;
localStorage.ambientMode = 'true';
setCanvasDimension(canvas, video);
paintStaticVideo(ctx, video);
document.getElementById('ambient-mode-toggle').innerHTML = '<span>Ambient Mode<span class="plyr__menu__value">On</span></span>';
}
}
function toggleAsiaServer() {
if (serverFallback) {
serverFallback = false;
localStorage.hstreamServerFallback = 'false';
document.getElementById('server-fallback-toggle').innerHTML = '<span>Fallback Server<span class="plyr__menu__value">Off</span></span>';
streamServers = apiResponse.stream_domains;
} else {
serverFallback = true;
localStorage.hstreamServerFallback = 'true';
document.getElementById('server-fallback-toggle').innerHTML = '<span>Fallback Server<span class="plyr__menu__value">On</span></span>';
streamServers = apiResponse.asia_stream_domains;
}
streamServerCount = streamServers.length;
streamServerIndex = Math.floor(Math.random() * streamServerCount);
streamServer = streamServers[streamServerIndex];
console.log('Selected Server: ' + streamServer);
if (player) {
clearInterval(saveInterval);
player.destroy();
}
initPlayer();
}
function initSubtitles(lang) {
if (isIOS()) {
return;
}
// Dispose old instance
if (subtitleInstance != null && subtitleInstance instanceof SubtitlesOctopus) {
subtitleInstance.dispose();
}
let newSubUrl = streamServer + '/' + apiResponse.stream_url + '/';
if (lang != 'en') {
newSubUrl += 'autotrans/' + lang + '.ass';
}
else {
newSubUrl += 'eng.ass'
}
let subFont = '/fonts/Figtree-ExtraBold.woff2';
// Hindi font
if (lang == 'hi') {
subFont = '/fonts/Hind-SemiBold.ttf';
}
// Subtitles
var options = {
video: document.getElementsByTagName('video')[0], // HTML5 video element
subUrl: newSubUrl, // Link to subtitles
workerUrl: '/build/js/subtitles-octopus-worker.js', // Link to WebAssembly-based file "libassjs-worker.js"
legacyWorkerUrl: '/build/js/subtitles-octopus-worker-legacy.js', // Link to non-WebAssembly worker
fonts: [subFont],
renderMode: 'wasm-blend',
};
subtitleInstance = new SubtitlesOctopus(options);
}
function initPlayer() {
player = new Plyr('#player', {
controls,
quality: {
default: 720,
options: [2161, 2160, 1081, 1080, 720]
},
i18n: {
qualityLabel: {
2161: "2160p48",
2160: "2160p",
1081: "1080p48",
1080: "1080p",
720: "720p"
},
qualityBadge: {
2161: "UHD@48",
1081: "FHD@48",
1080: "FHD",
},
},
fullscreen: { enabled: true, fallback: true, iosNative: true }
});
// Player Track Data
var data = addVideoTracks(streamServer, apiResponse, av1Supported, dashSupported);
player.source = {
type: 'video',
title: apiResponse.title,
poster: apiResponse.poster,
previewThumbnails: {
enabled: true,
src: streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt',
},
sources: data,
tracks: addSubtitleTracks(streamServer, apiResponse)
};
player.volume = volume;
//player.captions.languages = ['en'];
player.captions.language = 'en';
player.captions.active = captions;
if (dashSupported && !apiResponse.legacy) {
player.on('qualitychange', () => {
initDash(data, player);
});
initDash(data, player);
}
// Ambient Mode
let canvas = document.getElementById("ambientVideo"), ctx = canvas.getContext("2d"), video = document.getElementsByTagName('video')[0];
setCanvasDimension(canvas, video);
paintStaticVideo(ctx, video);
var allItems = document.getElementsByClassName('plyr__control--forward');
var lastItem = allItems[allItems.length - 1];
lastItem.insertAdjacentHTML('afterend', '<button id="ambient-mode-toggle" type="button" class="plyr__control" role="menuitem" aria-haspopup="true"><span>Ambient Mode<span class="plyr__menu__value">On</span></span></button>');
document.getElementById('ambient-mode-toggle').addEventListener('click', toggleAmbientMode);
if (localStorage.ambientMode == 'false') {
toggleAmbientMode();
}
// Server select (Asia)
lastItem = allItems[allItems.length - 1];
let value = 'Off';
if (serverFallback) { value = 'On'; }
lastItem.insertAdjacentHTML('afterend', '<button id="server-fallback-toggle" type="button" class="plyr__control" role="menuitem" aria-haspopup="true"><span>Fallback Server<span class="plyr__menu__value">' + value + '</span></span></button>');
document.getElementById('server-fallback-toggle').addEventListener('click', toggleAsiaServer);
var clickedPlay = false;
player.on('play', () => {
if (!clickedPlay) {
player.stop();
console.log("Stopped video, because user didn't click play.")
}
setCanvasDimension(canvas, video);
console.log('Play => Function Loop()');
var $this = video;
(function loop() {
if (!player.paused && !player.ended && localStorage.theme == 'dark' && ambientMode) {
ctx.drawImage($this, 0, 0, $this.offsetWidth, $this.offsetHeight);
setTimeout(loop, 24000 / 1001); // drawing at 30fps
}
})();
});
player.on('seeked', () => {
paintStaticVideo(ctx, video);
if (player.currentTime > 0) {
lastTime = player.currentTime;
}
console.log('Seeked => paintStaticVideo() at ' + player.currentTime);
});
window.addEventListener("resize", () => {
setCanvasDimension(canvas, video);
if (player.paused) {
paintStaticVideo(ctx, video);
}
});
player.on('captionsenabled', () => {
document.getElementsByClassName('libassjs-canvas-parent')[0].style.visibility = 'visible';
localStorage.setItem('hstreamCaptions', 'true');
console.log('Set Captions Status to Local Storage: true');
});
player.on('captionsdisabled', () => {
document.getElementsByClassName('libassjs-canvas-parent')[0].style.visibility = 'hidden';
localStorage.setItem('hstreamCaptions', 'false');
console.log('Set Captions Status to Local Storage: false');
});
player.on('volumechange', () => {
console.log('Saving Audio Volume to Local Storage: ' + player.volume);
localStorage.setItem('hstreamVolume', player.volume.toString())
});
player.on('ended', () => {
playNextPlaylistVideo();
});
player.on('languagechange', (event) => {
let lang = event.detail.plyr.captions.language;
console.log('Subtitle Event ' + lang);
initSubtitles(lang);
});
function playerPlayTemp() {
clickedPlay = true;
}
document.querySelectorAll('[data-plyr="play"]').forEach(play =>
play.addEventListener('click', playerPlayTemp)
);
document.getElementsByClassName('plyr--video')[0].addEventListener('click', playerPlayTemp);
initMobileWidescreen();
// Start time
setTimeout(function () {
const params = new URLSearchParams(window.location.search);
const time = parseInt(params.get("t"));
if (!isNaN(time)) {
player.currentTime = time;
console.log("Skipping to " + time)
}
if (lastTime > 0) {
player.currentTime = lastTime;
console.log("Skipping to " + lastTime)
}
}, 500);
player.on('ready', () => {
mobileDoubleClick(player);
});
// Server Select
// I hate this...
var settingElements = document.getElementsByClassName('plyr__control--forward');
if (settingElements.length == 3) {
settingElements[2].insertAdjacentHTML('afterend', serverSelectMenuItem(streamServerIndex));
var settingNodes = document.getElementsByClassName('plyr__menu__container')[0].childNodes[0].childNodes;
if (settingNodes.length == 4) {
document.getElementsByClassName('plyr__menu__container')[0].childNodes[0].childNodes[3].insertAdjacentHTML('afterend', serverSelectSubmenu(streamServerIndex, streamServerCount));
}
// Event Listeners
document.getElementById('server-select').addEventListener('click', serverSelectMenuClickToggle);
document.getElementById('server-select-list-back-btn').addEventListener('click', serverSelectMenuClickToggle);
let serverSelects = document.getElementsByClassName('change_server');
for (let i = 0; i < serverSelects.length; i++) {
serverSelects[i].addEventListener('click', function() {
streamServerIndex = Number(this.value);
streamServer = streamServers[streamServerIndex];
console.log('Selected Server: ' + streamServer);
if (player) {
clearInterval(saveInterval);
player.destroy();
}
initPlayer();
});
}
}
// Periodically save last timestamp
saveInterval = setInterval(function () {
lastTime = player.currentTime;
console.log("Last Player Position: " + lastTime);
}, 10000);
}
async function initVidstackPlayer() {
const videoSource = streamServer + '/' + apiResponse.stream_url + '/x264.720p.mp4';
const videoThumbs = streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt';
const videoCaption = streamServer + '/' + apiResponse.stream_url + '/eng.vtt';
player = await VidstackPlayer.create({
target: '#player',
title: apiResponse.title,
src: videoSource,
poster: apiResponse.poster,
layout: new VidstackPlayerLayout({
thumbnails: videoThumbs,
}),
tracks: [
{
src: videoCaption,
label: 'English',
language: 'en-US',
kind: 'subtitles',
type: 'vtt',
default: true,
}
]
});
// Ambient Mode
let canvas = document.getElementById("ambientVideo"), ctx = canvas.getContext("2d"), video = document.getElementsByTagName('video')[0];
setCanvasDimension(canvas, video);
paintStaticVideo(ctx, video);
player.addEventListener('play', () => {
setCanvasDimension(canvas, video);
console.log('Play => Function Loop()');
var $this = video;
(function loop() {
if (!player.paused && !player.ended && localStorage.theme == 'dark' && ambientMode) {
ctx.drawImage($this, 0, 0, $this.offsetWidth, $this.offsetHeight);
setTimeout(loop, 24000 / 1001); // drawing at 30fps
}
})();
});
}
// Get Data from API
window.axios.post('/player/api', {
episode_id: document.getElementById('e_id').value
}).then(function (response) {
if (response.status == 200) {
apiResponse = response.data;
streamServers = apiResponse.stream_domains;
if (serverFallback) {
streamServers = apiResponse.asia_stream_domains;
}
streamServerCount = streamServers.length;
streamServerIndex = Math.floor(Math.random() * streamServerCount);
streamServer = streamServers[streamServerIndex];
console.log('Selected Server: ' + streamServer + ' with Index: ' + streamServerIndex);
if (!isIOS()) {
initPlayer();
}
else {
console.log("Detected Apple Shit. Using different player.")
initVidstackPlayer();
}
}
}).catch(function (error) {
var alert = document.getElementById("player-alert");
alert.innerText = 'The player encountered a problem: ' + error;
alert.classList.remove("hidden");
});

120
resources/js/playlist.js Normal file
View File

@@ -0,0 +1,120 @@
export function playNextPlaylistVideo() {
console.log('Playing next episode');
if (!document.getElementById("playlist_id")) {
console.log('No playlist specified');
return;
}
var playlistId = document.getElementById("playlist_id").value;
var nextEpisode = document.getElementById("playlist_next_episode_slug").value;
if (nextEpisode === ""){
return;
}
window.location.href = '/hentai/' + nextEpisode + '?playlist=' + playlistId;
}
function deleteEntry(playlistId, episodeId) {
window.axios.post('/user/playlist-episode', {
playlist: playlistId,
episode: episodeId
}).then(function (response) {
if (response.status == 200) {
console.log(response);
if (response.data.message == 'success') {
Swal.fire({
title: "Deleted!",
text: "Removed entry from playlist!",
icon: "success",
confirmButtonText: "OK",
willClose: () => {
location.reload();
}
}).then((result) => {
if (result.isConfirmed) {
location.reload();
}
});
}
}
}).catch(function (error) {
Swal.fire({
title: "Error!",
text: error,
icon: "error"
});
console.log(error);
});
}
function addDesktopDeleteListener() {
const deleteButtons = document.querySelectorAll('[id^="delD"]');
deleteButtons.forEach(button => {
const playlist = button.id.split('-')[1];
const episode = button.id.split('-')[2];
console.log("Playlist: " + playlist + " Episode: " + episode);
button.addEventListener('click', () => deleteEntry(playlist, episode));
});
}
// Playlist Swipe (Delete)
document.addEventListener('DOMContentLoaded', () => {
const swipeContainers = document.querySelectorAll('.swipe-container');
var swipeOptions = {
dragLockToAxis: true,
dragBlockHorizontal: true
};
swipeContainers.forEach(container => {
const controls = new Hammer(container, swipeOptions);
const originalColor = container.style.backgroundColor;
const playlistId = container.id.split('-')[0];
const episodeId = container.id.split('-')[1];
const delIcon = document.getElementById('del-' + container.id);
// Set the initial position
let posX = 0;
// Listen for the pan gesture
controls.on('pan', (event) => {
// Update the X position based on the drag delta
posX = event.deltaX;
if (posX > 0) {
// Only allow left swipe
posX = 0;
}
// Apply the translation to the element
container.style.transform = `translateX(${posX}px)`;
container.style.backgroundColor = "rgba(159, 18, 18, 0.3)";
setTimeout(() => {
delIcon.classList.remove('fa-grip-lines-vertical');
delIcon.classList.add('fa-trash');
}, 300);
});
controls.on('panend', () => {
container.style.transition = 'transform 0.3s ease';
container.style.transform = 'translateX(0)';
setTimeout(() => {
container.style.transition = ''; // Reset transition for next drag
container.style.backgroundColor = originalColor;
delIcon.classList.remove('fa-trash');
delIcon.classList.add('fa-grip-lines-vertical');
}, 300);
});
controls.on('swipeleft', (event) => {
container.style.display = 'none';
console.log(playlistId, episodeId);
deleteEntry(playlistId, episodeId);
});
});
addDesktopDeleteListener();
});

56
resources/js/preview.js Normal file
View File

@@ -0,0 +1,56 @@
const sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));
var old_timestamp = document.getElementById('ts_reference').value;
function initPreviews() {
var thumbs = document.querySelectorAll('div[data-thumbs]');
thumbs.forEach(function (thumb) {
var thumbsJSON = JSON.parse(thumb.dataset.thumbs);
var originalImage = thumb.children[0].children[1].src;
var interval;
var i = 1;
function clear() {
thumb.children[0].children[1].src = originalImage;
i = 1;
clearTimeout(interval);
}
function toggle() {
if (i == 0) {
clear();
return;
}
thumb.children[0].children[1].src = thumbsJSON[i];
i = (i + 1) % thumbsJSON.length;
}
function interval() {
// Start Preview
interval = setInterval(toggle, 700);
}
thumb.addEventListener('mouseenter', interval);
thumb.addEventListener('mouseleave', clear);
});
}
async function init() {
for (let i = 0; i < 9; i++) {
var new_timestamp = document.getElementById('ts_reference').value;
if (new_timestamp != old_timestamp) {
console.log('== Changed ==');
initPreviews();
break;
}
console.log('== Didnt Change ==');
await sleep(1000);
}
}
window.addEventListener('contentChanged', event => {
console.log('== Received contentChanged Event ==');
init();
});
initPreviews();

22
resources/js/theme.js Normal file
View File

@@ -0,0 +1,22 @@
function darkModeListener() {
document.querySelector("html").classList.toggle("dark");
if (localStorage.theme == 'light') {
localStorage.theme = 'dark';
} else {
localStorage.theme = 'light';
}
}
document.querySelector("input[type='checkbox']#toogleTheme").addEventListener("click", darkModeListener);
if(localStorage.theme) {
if (localStorage.theme == 'light') {
if (document.querySelector("html").classList.contains('dark')) {
document.querySelector("html").classList.toggle("dark");
}
document.getElementById("toogleTheme").checked = true;
}
} else {
// Default Dark Theme
localStorage.theme = 'dark';
}

113
resources/js/upload.js Normal file
View File

@@ -0,0 +1,113 @@
import Tagify from '@yaireo/tagify';
import '@yaireo/tagify/dist/tagify.css';
const taginput = document.querySelector("#tags");
const studioinput = document.querySelector("#studio");
// Get Tags from API
window.axios.get('/admin/tags').then(function (response) {
if (response.status != 200) {
return;
}
new Tagify(taginput, {
whitelist: response.data.tags,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true
}
});
}).catch(function (error) {
console.log(error);
});
// Get Studios from API
window.axios.get('/admin/studios').then(function (response) {
if (response.status != 200) {
return;
}
new Tagify(studioinput, {
whitelist: response.data.studios,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true
}
});
}).catch(function (error) {
console.log(error);
});
let eps = 1;
function dynEpisode() {
let amount = this.value;
if (amount > eps) {
eps += 1;
var episodeUploads = `
<div class="grid grid-cols-2" id="dynU` + eps + `">
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodecover` + eps + `">Cover ` + eps + `:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodecover` + eps + `" id="episodecover` + eps + `" required>
</div>
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodegallery` + eps + `">Gallery ` + eps + `:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodegallery` + eps + `[]" id="episodegallery` + eps + `" multiple="">
</div>
</div>
<div class="p-4 pt-0" id="dynB` + eps + `">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="description` + eps + `">Description ` + eps + `:</label>
<textarea rows="4" cols="50" id="description` + eps + `" name="description` + eps + `" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm" required>
</textarea>
</div>
<div class="p-4 pt-0" id="dynD` + eps + `">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl` + eps + `">Download 1080p ` + eps + `:</label>
<input class="border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm block w-full" id="episodedlurl` + eps + `" type="text" name="episodedlurl` + eps + `" required="required">
</div>
<div class="p-4 pt-0" id="dynD48fps` + eps + `">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurlinterpolated` + eps + `">Download 1080p48fps ` + eps + `:</label>
<input class="border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm block w-full" id="episodedlurlinterpolated` + eps + `" type="text" name="episodedlurlinterpolated` + eps + `">
</div>
<div class="p-4 pt-0" id="dynD4k` + eps + `">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl4k` + eps + `">Download 4k ` + eps + `:</label>
<input class="border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm block w-full" id="episodedlurl4k` + eps + `" type="text" name="episodedlurl4k` + eps + `" required="required">
</div>
<div class="p-4 pt-0" id="dynDUHD48fps` + eps + `">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="downloadUHDi` + eps + `">Download 4k 48fps ` + eps + `:</label>
<input class="border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm block w-full" id="downloadUHDi` + eps + `" type="text" name="downloadUHDi` + eps + `">
</div>
`;
var element = document.getElementById('moreEpisodes');
element.innerHTML = element.innerHTML + episodeUploads;
} else if (amount < eps) {
if (amount == 0) {
this.value = 1;
return;
}
document.getElementById("dynU" + eps).remove();
document.getElementById("dynD" + eps).remove();
document.getElementById("dynD4k" + eps).remove();
document.getElementById("dynD48fps" + eps).remove();
document.getElementById("dynDUHD48fps" + eps).remove();
document.getElementById("dynB" + eps).remove();
eps -= 1;
}
}
document.getElementById("episodes").addEventListener('change', dynEpisode);

View File

@@ -0,0 +1,28 @@
import Tagify from '@yaireo/tagify';
import '@yaireo/tagify/dist/tagify.css';
const taginput = document.querySelector("#tags");
// Get Tags from API
window.axios.get('/user/blacklist').then(function (response) {
if (response.status != 200) {
return;
}
var tagify = new Tagify(taginput, {
whitelist: response.data.tags,
enforceWhitelist: true,
dropdown: {
classname: "color-blue",
enabled: 0, // show the dropdown immediately on focus
maxItems: 10,
position: "text", // place the dropdown near the typed text
closeOnSelect: false, // keep the dropdown open after selecting a suggestion
highlightFirst: true,
}
});
tagify.addTags(response.data.usertags);
}).catch(function (error) {
console.log(error);
});

View File

@@ -0,0 +1,32 @@
<x-app-layout>
<div class="p-5">
<div class="w-[50%] mx-auto sm:px-6 lg:px-8 text-gray-800 dark:text-gray-200 bg-white dark:bg-neutral-950 rounded-lg">
<div class="relative p-4">
<form method="POST" action="{{ route('admin.add.torrent') }}">
@csrf
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Hentai ID:</label>
<input id="hentai_id" name="hentai_id" class="block w-full p-4 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" value="{{ $hentai_id }}" required>
</div>
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Torrent URL:</label>
<input id="torrent_url" name="torrent_url" class="block w-full p-4 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" required>
</div>
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Episodes (e.g. "01-02" or "01"):</label>
<input id="torrent_episodes" name="torrent_episodes" class="block w-full p-4 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" required>
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create
</button>
</div>
</form>
</div>
</div>
</div>
</x-app-layout>

View File

@@ -0,0 +1,41 @@
@extends('admin.layout')
@section('content')
<div class="p-4">
<!-- Current alerts -->
@include('admin.home.alert')
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<div class="flex items-center justify-center">
<div class="relative p-4 pt-0 bg-white dark:bg-neutral-800 rounded-lg border-t-2 border-b-2 border-pink-700">
<div class="relative p-4 w-96">
<form method="POST" action="{{ route('admin.alert.create') }}">
@csrf
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="message">Enter Alert Message Here:</label>
<x-text-input id="message" class="block mt-1 w-full" type="text" name="message" required autofocus/>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
</div>
<div class="mt-5 p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="type">Type:</label>
<select name="type" id="type" class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="0">Info</option>
<option value="1" selected>Warning</option>
</select>
<x-input-error :messages="$errors->get('type')" class="mt-2" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,39 @@
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<div class="flex items-center justify-center">
<div class="text-gray-800 dark:text-gray-200 bg-transparent mt-5">
<div class="relative p-4 pt-0 bg-white dark:bg-neutral-800 rounded-lg border-t-2 border-b-2 border-pink-700">
<form method="POST" action="{{ route('admin.background.create') }}" enctype="multipart/form-data">
@csrf
<div class="flex flex-grow">
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="images">Image(s):</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="images[]" id="images" multiple>
</div>
<div class="p-4 flex-1">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="date_start">Start Date:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="date" name="date_start" id="date_start" required>
</div>
<div class="p-4 flex-1">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="date_end">End Date:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="date" name="date_end" id="date_end" required>
</div>
<div class="p-4 pt-10">
<label>
<input type="checkbox" class="w-5 h-5 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" name="default" value="1"> Default
</label>
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,9 @@
@extends('admin.layout')
@section('content')
<!-- Create entry -->
@include('admin.background.create')
<!-- List entries -->
@livewire('background-images')
@endsection

View File

@@ -0,0 +1,44 @@
@extends('admin.layout')
@section('content')
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<div class="flex items-center justify-center">
<div class="relative overflow-x-auto rounded-lg w-3/6">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-white">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-pink-700 dark:text-neutral-200">
<tr>
<th class="px-6 py-3">Name</th>
<th class="px-6 py-3">Subject</th>
<th class="px-6 py-3">Message</th>
<th class="px-6 py-3">Date</th>
<th class="px-6 py-3">Action</th>
</tr>
</thead>
<tbody>
@foreach($contacts as $contact)
<tr class="bg-white border-t dark:bg-neutral-800 dark:border-pink-700">
<td class="px-6 py-4">
{{ $contact->name }}
<br>
{{ $contact->email }}
</td>
<td class="px-6 py-4">{{ $contact->subject }}</td>
<td class="px-6 py-4">{{ $contact->message }}</td>
<td class="px-6 py-4">{{ $contact->created_at->format('Y/m/d') }}</td>
<td class="px-6 py-4">
<form method="POST" action="{{ route('admin.contact.delete', $contact->id) }}">
@csrf
@method('delete')
<button type="submit" class="inline-block rounded bg-rose-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
Delete
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,37 @@
@php $alerts = \cache()->remember('alerts', 300, fn () => \App\Models\Alert::latest()->get()); @endphp
@foreach($alerts as $alert)
<div class="mx-auto pt-2 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%] relative z-10">
@if ($alert->type == 0)
<div class="mb-4 rounded-lg bg-success-400 px-6 py-5 text-base text-success-800 mt-5" role="alert">
{{ $alert->text }}
@auth
@if(Auth::user()->is_admin)
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-success-900">
@csrf
@method('delete')
<button type="submit" class="cursor-pointer transition duration-150 ease-in-out" data-te-ripple-init data-te-ripple-color="light">
<i class="fa-solid fa-xmark"></i>
</button>
</form>
@endif
@endauth
</div>
@elseif ($alert->type == 1)
<div class="mb-4 rounded-lg bg-danger-400 px-6 py-5 text-base text-danger-800 mt-5" role="alert">
{{ $alert->text }}
@auth
@if(Auth::user()->is_admin)
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-danger-900">
@csrf
@method('delete')
<button type="submit" class="cursor-pointer transition duration-150 ease-in-out" data-te-ripple-init data-te-ripple-color="light">
<i class="fa-solid fa-xmark"></i>
</button>
</form>
@endif
@endauth
</div>
@endif
</div>
@endforeach

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="scroll-smooth">
@include('partials.head')
<body class="font-sans antialiased">
<div class="flex flex-col min-h-screen bg-gray-100 dark:bg-neutral-900">
@include('layouts.navigation')
@include('user.partials.background')
<div class="mt-[65px]">
@include('admin.partials.sidenav')
<div class="pl-64">
@yield('content')
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalAddSubtitles" tabindex="-1" aria-labelledby="Upload" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[60%] xl:min-[576px]:max-w-[50%] 2xl:min-[576px]:max-w-[40%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Add Subtitles')"/>
<!--Modal body-->
<div class="relative p-4 pt-0">
<form method="POST" action="{{ route('admin.update.subtitles') }}">
@csrf
<div class="p-2 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="subtitles">Subtitles:</label>
<x-text-input id="subtitles" class="block w-full" type="text" name="subtitles" />
</div>
<input name="episode_id" id="episode_id" type="hidden" value="{{ $episode->id }}" />
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Edit
</button>
</div>
</form>
<form method="POST" action="{{ route('admin.add.new.subtitle') }}">
@csrf
<div class="grid grid-cols-2">
<div class="p-2 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="subtitles">Name:</label>
<x-text-input id="name" class="block w-full" type="text" name="name" required />
</div>
<div class="p-2 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="subtitles">Slug:</label>
<x-text-input id="slug" class="block w-full" type="text" name="slug" required />
</div>
</div>
<input name="episode_id" id="episode_id" type="hidden" value="{{ $episode->id }}" />
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Add
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modals JS -->
@vite(['resources/js/admin-subtitles.js'])
</div>

View File

@@ -0,0 +1,113 @@
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalEditEpisode" tabindex="-1" aria-labelledby="Upload" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[50%]">
<div class="flex relative flex-col w-full text-current bg-clip-padding bg-white rounded-md border-none shadow-lg outline-none pointer-events-auto dark:bg-neutral-800">
<x-modal-header :title="__('Edit Episode')"/>
<!--Modal body-->
<div class="relative p-4 pt-0">
<form method="POST" action="{{ route('admin.edit') }}" enctype="multipart/form-data">
@csrf
<div class="grid grid-cols-3">
<div class="col-span-2">
<!-- Tags -->
<div class="row-span-2 p-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="tags">Tags:</label>
<x-text-input id="tags" class="block w-full" type="text" name="tags" required />
</div>
</div>
<div class="grid grid-rows-2">
<!-- Studio -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="studio">Studio:</label>
<x-text-input id="studio" class="block w-full" type="text" name="studio" value="{{ $episode->studio->name }}" required />
</div>
<div class="grid grid-cols-3">
<!-- Interpolation -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="interpolated">Interpolated:</label>
<select class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" name="interpolated" id="interpolated" required>
<option value="yes" @if ($episode->interpolated == true) selected @endif>Yes</option>
<option value="no" @if ($episode->interpolated == false) selected @endif>No</option>
</select>
</div>
<!-- DVD -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="dvd">DVD Aspect:</label>
<select class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" name="dvd" id="dvd" required>
<option value="yes" @if ($episode->is_dvd_aspect == true) selected @endif>Yes</option>
<option value="no" @if ($episode->is_dvd_aspect == false) selected @endif>No</option>
</select>
</div>
<!-- Date -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="releasedate">Release Date:</label>
<input class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="date" name="releasedate" id="releasedate" value="{{ $episode->release_date }}" required>
</div>
</div>
</div>
</div>
<!-- Stream URL -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="baseurl">Stream:</label>
<x-text-input id="baseurl" class="block w-full" type="text" name="baseurl" value="{{ $episode->url }}" required />
</div>
<input name="episode_id" id="episode_id" type="hidden" value="{{ $episode->id }}" />
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="description">Description:</label>
<textarea rows="4" cols="50" id="description" name="description" class="block mt-1 w-full rounded-md border-gray-300 shadow-sm dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600" required>{{ $episode->description }}</textarea>
</div>
<!-- Episodes -->
<div class="grid grid-cols-2">
<!-- Cover -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="episodecover1">Cover 1:</label>
<input class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodecover1" id="episodecover1">
</div>
<!-- Thumbs -->
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="episodegallery1">Gallery 1:</label>
<input class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodegallery1[]" id="episodegallery1" multiple="">
</div>
</div>
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="episodedlurl1">Download 1080p:</label>
<x-text-input id="episodedlurl1" class="block w-full" type="text" name="episodedlurl1" value="{{ $episode->getDownloadByType('FHD')->url ?? '' }}" required />
</div>
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="episodedlurlinterpolated1">Download 1080p Interpolated:</label>
<x-text-input id="episodedlurlinterpolated1" class="block w-full" type="text" name="episodedlurlinterpolated1" value="{{ $episode->getDownloadByType('FHDi')->url ?? '' }}" />
</div>
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="episodedlurl4k1">Download 4k:</label>
<x-text-input id="episodedlurl4k1" class="block w-full" type="text" name="episodedlurl4k1" value="{{ $episode->getDownloadByType('UHD')->url ?? '' }}" />
</div>
<div class="p-2 pt-0">
<label class="w-full leading-tight text-gray-800 dark:text-gray-200" for="downloadUHDi1">Download 4k Interpolated:</label>
<x-text-input id="downloadUHDi1" class="block w-full" type="text" name="downloadUHDi1" value="{{ $episode->getDownloadByType('UHDi')->url ?? '' }}" />
</div>
<div class="flex flex-wrap flex-shrink-0 justify-end items-center p-4 rounded-b-md">
<button type="button" class="inline-block px-6 pt-2.5 pb-2 text-xs font-medium leading-normal uppercase rounded transition duration-150 ease-in-out bg-primary-100 text-primary-700 hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</button>
<button type="submit" class="inline-block px-6 pt-2.5 pb-2 ml-1 text-xs font-medium leading-normal text-white uppercase bg-rose-600 rounded transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Edit
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modals JS -->
@vite(['resources/js/admin-edit.js'])
</div>

View File

@@ -0,0 +1,74 @@
<!--Verically centered modal-->
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalUploadEpisode" tabindex="-1" aria-labelledby="Upload" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[50%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Upload Episode')"/>
<!--Modal body-->
<div class="relative p-4 pt-0">
<form method="POST" action="{{ route('admin.upload.episode') }}" enctype="multipart/form-data">
@csrf
<input name="episode_id" id="episode_id" type="hidden"/>
<!-- Episodes -->
<div class="grid grid-cols-2">
<!-- Cover -->
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodecover1">Cover:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodecover1" id="episodecover1" required>
</div>
<!-- Thumbs -->
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodegallery1">Gallery:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodegallery1[]" id="episodegallery1" multiple="">
</div>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="description">Description:</label>
<textarea rows="4" cols="50" id="description" name="description" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm" required>{{ $episode->description }}</textarea>
</div>
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="baseurl">Stream:</label>
<x-text-input id="baseurl" class="block w-full" type="text" name="baseurl" required />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl1">Download 1080p:</label>
<x-text-input id="episodedlurl1" class="block w-full" type="text" name="episodedlurl1" required />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurlinterpolated1">Download 1080p 48fps:</label>
<x-text-input id="episodedlurlinterpolated1" class="block w-full" type="text" name="episodedlurlinterpolated1" />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl4k1">Download 4k:</label>
<x-text-input id="episodedlurl4k1" class="block w-full" type="text" name="episodedlurl4k1" />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="downloadUHDi1">Download 4k 48fps:</label>
<x-text-input id="downloadUHDi1" class="block w-full" type="text" name="downloadUHDi1" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="button" class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</button>
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Add
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modals JS -->
<script>
document.getElementById('episode_id').value = document.getElementById('e_id').value;
</script>
</div>

View File

@@ -0,0 +1,36 @@
<aside id="default-sidebar" class="fixed left-0 z-40 w-64 h-screen transition-transform -translate-x-full sm:translate-x-0" aria-label="Sidebar">
<div class="h-full px-3 py-4 overflow-y-auto bg-white/30 dark:bg-neutral-950/40 backdrop-blur">
<ul class="space-y-2 font-medium">
<li>
<a href="{{ route('admin.upload.index') }}" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-pink-700/40 group @if(Route::is('admin.upload.index')) bg-pink-700/40 @endif">
<i class="fa-solid fa-upload"></i>
<span class="ms-3">Create Release</span>
</a>
</li>
<li>
<a href="{{ route('admin.alert.index') }}" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-pink-700/40 group @if(Route::is('admin.alert.index')) bg-pink-700/40 @endif">
<i class="fa-solid fa-bell"></i>
<span class="ms-3">Alerts</span>
</a>
</li>
<li>
<a href="{{ route('admin.user.index') }}" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-pink-700/40 group @if(Route::is('admin.user.index')) bg-pink-700/40 @endif">
<i class="fa-solid fa-user"></i>
<span class="ms-3">Users</span>
</a>
</li>
<li>
<a href="{{ route('admin.contact.index') }}" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-pink-700/40 group @if(Route::is('admin.contact.index')) bg-pink-700/40 @endif">
<i class="fa-solid fa-message"></i>
<span class="ms-3">Contacts</span>
</a>
</li>
<li>
<a href="{{ route('admin.background.index') }}" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-pink-700/40 group @if(Route::is('admin.background.index')) bg-pink-700/40 @endif">
<i class="fa-solid fa-images"></i>
<span class="ms-3">Backgrounds</span>
</a>
</li>
</ul>
</div>
</aside>

View File

@@ -0,0 +1,105 @@
@extends('admin.layout')
@section('content')
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<div class="flex items-center justify-center">
<div class="relative p-4 pt-0 bg-white dark:bg-neutral-800 rounded-lg border-t-2 border-b-2 border-pink-700">
<form method="POST" action="{{ route('admin.upload') }}" enctype="multipart/form-data">
@csrf
<div class="grid grid-cols-3">
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="title">Title:</label>
<x-text-input id="title" class="block w-full" type="text" name="title" required autofocus/>
</div>
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="title_jpn">Title JPN:</label>
<x-text-input id="title_jpn" class="block w-full" type="text" name="title_jpn" required />
</div>
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="studio">Studio:</label>
<x-text-input id="studio" class="block w-full" type="text" name="studio" required />
</div>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="tags">Tags:</label>
<x-text-input id="tags" class="block w-full" type="text" name="tags" required />
</div>
<div class="grid grid-cols-2">
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="releasedate">Release Date:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="date" name="releasedate" id="releasedate" required>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodes">Episodes:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="number" name="episodes" id="episodes" value="1" required>
</div>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="baseurl">Stream:</label>
<x-text-input id="baseurl" class="block w-full" type="text" name="baseurl" required />
</div>
<!-- Episodes -->
<div class="grid grid-cols-2">
<!-- Cover -->
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodecover1">Cover 1:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodecover1" id="episodecover1" required>
</div>
<!-- Thumbs -->
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodegallery1">Gallery 1:</label>
<input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" type="file" name="episodegallery1[]" id="episodegallery1" multiple="">
</div>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="description1">Description 1:</label>
<textarea rows="4" cols="50" id="description1" name="description1" class="mt-1 block w-full border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm" required>
</textarea>
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl1">Download 1080p 1:</label>
<x-text-input id="episodedlurl1" class="block w-full" type="text" name="episodedlurl1" required />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurlinterpolated1">Download 1080p48fps 1:</label>
<x-text-input id="episodedlurlinterpolated1" class="block w-full" type="text" name="episodedlurlinterpolated1" />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="episodedlurl4k1">Download 4k 1:</label>
<x-text-input id="episodedlurl4k1" class="block w-full" type="text" name="episodedlurl4k1" required />
</div>
<div class="p-4 pt-0">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="downloadUHDi1">Download 4k 48fps 1:</label>
<x-text-input id="downloadUHDi1" class="block w-full" type="text" name="downloadUHDi1" />
</div>
<div id="moreEpisodes">
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="button" class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</button>
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create
</button>
</div>
</form>
</div>
</div>
</div>
@vite(['resources/js/upload.js'])
@endsection

View File

@@ -0,0 +1,19 @@
@auth
@if(Auth::user()->is_admin)
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
<div class="float-left">
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
<i class="fa-solid fa-plus pr-[6px]"></i> Add Episode
</a>
</div>
<div class="float-right">
<a data-te-toggle="modal" data-te-target="#modalAddSubtitles" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
<i class="fa-solid fa-plus pr-[6px]"></i> Add Subtitles
</a>
<a data-te-toggle="modal" data-te-target="#modalEditEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
<i class="fa-solid fa-pen pr-[6px]"></i> Edit Episode
</a>
</div>
</div>
@endif
@endauth

View File

@@ -0,0 +1,5 @@
@extends('admin.layout')
@section('content')
@livewire('admin-user-search')
@endsection

View File

@@ -0,0 +1 @@
<p>You have been banned</p>

View File

@@ -0,0 +1,20 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
{{ __('This is a secure area of the application. Please confirm your session before continuing.') }}
</div>
<form method="POST" action="{{ route('larascord.refresh_token') }}">
@csrf
<div class="flex justify-end mt-4">
<x-primary-button>
<svg style="margin-right: 10px;" width="30px" height="30px" viewBox="0 -28.5 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z" fill="#ffff" fill-rule="nonzero"></path>
</g>
</svg>
{{ __('Confirm') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>

View File

@@ -0,0 +1,7 @@
<x-app-layout>
<div class="container my-24 mx-auto md:px-6 z-10 relative">
<p class="leading-normal font-bold text-lg text-rose-600 text-center">
(╥﹏╥)
</p>
</div>
</x-app-layout>

View File

@@ -0,0 +1 @@
<img class="h-10" src="/images/hs_banner.png">

View File

@@ -0,0 +1,7 @@
@props(['status'])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}>
{{ $status }}
</div>
@endif

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1,3 @@
<a href="{{ route('login') }}" class="bg-blue-700 hover:bg-blue-600 text-white font-bold py-2 px-4 h-10 rounded " style="width: 102px">
<i class="fa-brands fa-discord"></i> Log in
</a>

View File

@@ -0,0 +1,3 @@
<a href="{{ config('discord.invite_link') }}" class="bg-blue-700 hover:bg-blue-600 text-white font-bold py-2 px-4 h-10 rounded " style="width: 102px">
<i class="fa-brands fa-discord"></i> Join Server
</a>

View File

@@ -0,0 +1 @@
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-neutral-100 dark:hover:bg-neutral-900 focus:outline-none focus:bg-neutral-100 dark:focus:bg-neutral-800 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>

View File

@@ -0,0 +1,43 @@
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-neutral-800'])
@php
switch ($align) {
case 'left':
$alignmentClasses = 'origin-top-left left-0';
break;
case 'top':
$alignmentClasses = 'origin-top';
break;
case 'right':
default:
$alignmentClasses = 'origin-top-right right-0';
break;
}
switch ($width) {
case '48':
$width = 'w-48';
break;
}
@endphp
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
<div @click="open = ! open">
{{ $trigger }}
</div>
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
style="display: none;"
@click="open = false">
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
{{ $content }}
</div>
</div>
</div>

View File

@@ -0,0 +1,58 @@
@props(['episode'])
<div class="relative p-1 mb-8 w-full transition duration-300 ease-in-out md:p-2 hover:-translate-y-1 hover:scale-110">
<a class="hover:text-blue-600" href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ $episode->gallery->first()->thumbnail_url }}"></img>
@guest
<p
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<p
class="absolute left-4 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i class="fa-regular fa-heart"></i>
{{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@endguest
@php $problematic = cache()->rememberForever('episodeProblematic'.$episode->id, fn () => $episode->getProblematicTags()); @endphp
@if (!empty($problematic))
<p
class="absolute left-4 top-2 bg-red-700/70 !text-white rounded-br-lg rounded-tl-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-solid fa-triangle-exclamation"></i> {{ $problematic }}
</p>
@endif
@auth
@if ($episode->userWatched(auth()->user()->id))
<p
class="absolute right-2 top-2 bg-green-600/80 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<p
class="absolute left-2 bottom-2 bg-green-600/80 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@else
<p
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<p
class="absolute left-2 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@endif
@endauth
<div class="absolute w-[95%] grid grid-cols-1 text-center">
<p class="text-sm text-center text-black dark:text-white">{{ $episode->title }} -
{{ $episode->episode }}</p>
</div>
</a>
</div>

View File

@@ -0,0 +1,9 @@
@props(['messages'])
@if ($messages)
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}>
@foreach ((array) $messages as $message)
<li>{{ $message }}</li>
@endforeach
</ul>
@endif

View File

@@ -0,0 +1,5 @@
@props(['value'])
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700 dark:text-gray-300']) }}>
{{ $value ?? $slot }}
</label>

View File

@@ -0,0 +1,13 @@
@props(['title'])
<div class="flex flex-shrink-0 items-center justify-between rounded-t-md p-4 bg-rose-600">
<!--Modal title-->
<h5 class="text-xl font-medium leading-normal text-white">
{{ $title }}
</h5>
<!--Close button-->
<button type="button" class="box-content text-white rounded-none border-none hover:no-underline hover:opacity-75 focus:opacity-100 focus:shadow-none focus:outline-none" data-te-modal-dismiss aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-6 w-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>

View File

@@ -0,0 +1,77 @@
@props([
'name',
'show' => false,
'maxWidth' => '2xl'
])
@php
$maxWidth = [
'sm' => 'sm:max-w-sm',
'md' => 'sm:max-w-md',
'lg' => 'sm:max-w-lg',
'xl' => 'sm:max-w-xl',
'2xl' => 'sm:max-w-2xl',
][$maxWidth];
@endphp
<div
x-data="{
show: @js($show),
focusables() {
// All focusable element types...
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
return [...$el.querySelectorAll(selector)]
// All non-disabled elements...
.filter(el => ! el.hasAttribute('disabled'))
},
firstFocusable() { return this.focusables()[0] },
lastFocusable() { return this.focusables().slice(-1)[0] },
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
}"
x-init="$watch('show', value => {
if (value) {
document.body.classList.add('overflow-y-hidden');
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
} else {
document.body.classList.remove('overflow-y-hidden');
}
})"
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
x-on:close.stop="show = false"
x-on:keydown.escape.window="show = false"
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
x-show="show"
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
style="display: {{ $show ? 'block' : 'none' }};"
>
<div
x-show="show"
class="fixed inset-0 transform transition-all"
x-on:click="show = false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div>
</div>
<div
x-show="show"
class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
{{ $slot }}
</div>
</div>

View File

@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-rose-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-rose-700 active:bg-rose-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1,12 @@
@props(['active'])
@php
$classes =
$active ?? false
? 'block w-full pl-3 pr-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-left text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out'
: 'block w-full pl-3 pr-4 py-2 border-l-4 border-transparent text-left text-base font-medium text-neutral-600 dark:text-neutral-200 hover:text-neutral-800 dark:hover:text-neutral-200 hover:bg-neutral-50 dark:hover:bg-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600 focus:outline-none focus:text-neutral-800 dark:focus:text-neutral-200 focus:bg-neutral-50 dark:focus:bg-neutral-700 focus:border-neutral-300 dark:focus:border-neutral-600 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1,3 @@
@props(['disabled' => false])
<input {{ $disabled ? 'disabled' : '' }} {!! $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm']) !!}>

View File

@@ -0,0 +1,4 @@
@props(['disabled' => false])
<textarea rows="5" {{ $disabled ? 'disabled' : '' }} {!! $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm']) !!}>
</textarea>

View File

@@ -0,0 +1,15 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Contact') }}
</h2>
</x-slot>
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white dark:bg-neutral-800 shadow sm:rounded-lg">
<div class="max-w-xl">
@include('contact.partials.submit-contact-form')
</div>
</div>
</div>
</x-app-layout>

View File

@@ -0,0 +1,82 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
<i class="fa-solid fa-message"></i> {{ __('Contact Form') }}
</h2>
</header>
<form method="post" action="{{ route('contact.store') }}" class="mt-6 space-y-6">
@csrf
<p class="text-red-600">You won't get a reply here. Please contact us on Discord instead if a answer is required.</p>
<p class="text-red-600">Don't request hentai here!</p>
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" required autocomplete="name" />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" required autocomplete="email" />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
</div>
<div>
<x-input-label for="subject" :value="__('Subject')" />
<x-text-input id="subject" name="subject" type="text" class="mt-1 block w-full" required />
<x-input-error class="mt-2" :messages="$errors->get('subject')" />
</div>
<div>
<x-input-label for="message" :value="__('Message')" />
<x-textarea-input id="message" name="message" class="mt-1 block w-full" required />
<x-input-error class="mt-2" :messages="$errors->get('message')" />
</div>
<div>
<x-input-label for="message" :value="__('Captcha')" />
<div class="flex pt-2">
<div id="captchaImg">
{!! captcha_img() !!}
</div>
<button type="button" class="inline-flex items-center ml-2 px-2 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150" id="reloadcaptcha">
<i class="fa-solid fa-rotate-right"></i>
</button>
</div>
<br>
<x-text-input id="captcha" class="block " type="text" name="captcha" required />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Submit') }}</x-primary-button>
@if ($errors->any())
@foreach ($errors->all() as $error)
@if ($error == 'validation.captcha')
<p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 3000)" class="text-sm text-red-600 dark:text-red-400">Captcha Incorrect!</p>
@else
<p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 3000)" class="text-sm text-red-600 dark:text-red-400">{{ $error }}</p>
@endif
@endforeach
@endif
@if (session('status') === 'contact-submitted')
<p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 3000)" class="text-sm text-green-600 dark:text-green-400">{{ __('Your message was submitted.') }}</p>
@endif
</div>
</form>
<script>
function reloadCaptcha() {
window.axios.get('/reload-captcha').then(function(response) {
if (response.status == 200) {
document.querySelector("#captchaImg").innerHTML = response.data.captcha;
}
}).catch(function(error) {
console.log(error);
});
}
document.querySelector("#reloadcaptcha").addEventListener("click", reloadCaptcha);
</script>
</section>

View File

@@ -0,0 +1,6 @@
<x-app-layout>
<div class="flex w-full justify-center items-center" style="height: 80vh;">
<img src="/images/404.png">
</img>
</div>
</x-app-layout>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@include('partials.head')
<body class="font-sans antialiased">
<div class="flex flex-col min-h-screen bg-gray-100 dark:bg-neutral-900">
<div class="flex w-full justify-center items-center" style="height: 80vh;">
<div class="flex w-full justify-center items-center" style="height: 80vh;">
<img src="/images/500.png">
</img>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@include('partials.head')
<body class="font-sans antialiased">
<div class="flex flex-col min-h-screen bg-gray-100 dark:bg-neutral-900">
<div class="flex w-full justify-center items-center" style="height: 80vh;">
<div class="grid grid-rows-2 text-center">
<img src="/images/hs_banner.png">
<h1 class="font-semibold text-2xl text-gray-800 dark:text-gray-200 leading-tight pt-5">We are currently doing some maintenance.</h1>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,32 @@
<x-app-layout>
@include('home.partials.branding')
@include('admin.home.alert')
<!-- Recently Released -->
<div class="mx-auto pt-2 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%] z-10">
@include('home.partials.tabs-top')
</div>
<br><br>
<!-- Recently Uploaded -->
<div class="mx-auto sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%]">
@include('home.partials.tabs-middle')
</div>
<!-- Categories -->
<div class="mx-auto pt-6 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%]">
@include('home.partials.categories')
</div>
<!-- Random -->
<div class="mx-auto pt-6 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%]">
@include('home.partials.random')
</div>
<!-- Comments -->
<div class="mx-auto pt-6 sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[85%]">
@include('home.partials.comments')
</div>
</x-app-layout>

View File

@@ -0,0 +1,50 @@
<div class="mx-auto space-y-6 z-10 absolute">
<div class="flex !m-0 justify-center pt-[5vh] md:pt-[15vh] w-[99vw]">
<img src="/images/cropped-HS-1-270x270.webp" loading="lazy" height="133px" width="133px">
</div>
<div class="flex !m-0 justify-center h-[4vh] md:h-[7vh] w-[99vw]">
<h1 class="font-bold text-2xl md:text-4xl whitespace-nowrap dark:text-white">hstream.moe</h1>
</div>
<div class="flex !m-0 justify-center h-[4vh] md:h-[7vh] w-[99vw]">
<h2 class="text-1xl md:text-3xl font-extralight dark:text-white">{{ __('home.branding') }}</h2>
</div>
</div>
<div class="mx-auto space-y-6 h-[30vh] sm:h-[40vh] md:h-[60vh] z-10 overflow-hidden">
<div class="!m-0">
@php
$array = \cache()->remember('background', 300, function () {
$bg = new \App\Models\SiteBackground();
return $bg->getImages();
});
@endphp
@if ($array->isNotEmpty())
@php
$imageId = $array->random();
@endphp
<img
class="fade-img sm:relative md:absolute top-0 w-screen"
src="/images/background/{{ $imageId }}-1080p.webp"
srcset="/images/background/{{ $imageId }}-640p.webp 640w,
/images/background/{{ $imageId }}-720p.webp 720w,
/images/background/{{ $imageId }}-1080p.webp 1080w,
/images/background/{{ $imageId }}-1440p.webp 1440w"
sizes="(max-width: 640px) 640px,
(max-width: 1080px) 720px,
(max-width: 1440px) 1080px,
1440px"
loading="lazy"
height="640px"
>
@else
<img
class="fade-img sm:relative md:absolute top-0 w-screen"
src="/images/hs_branding_1.webp"
loading="lazy"
>
@endif
</div>
</div>

View File

@@ -0,0 +1,61 @@
<p class="leading-normal font-bold text-lg text-neutral-800 dark:text-white">
{{ __('home.categories') }}
</p>
@php
$categories = [
'Uncensored' => 'uncensored',
'Milf' => 'milf',
'Maid' => 'maid',
'School Girl' => 'school-girl',
'Succubus' => 'succubus',
'Tentacle' => 'tentacle',
'Big Boobs' => 'big-boobs',
'BDSM' => 'bdsm',
'Elf' => 'elf',
'4k 48fps' => '4k-48fps',
];
@endphp
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-5 lg:grid-cols-5 xl:grid-cols-5 2xl:grid-cols-5 gap-2">
@foreach ($categories as $name => $slug)
@php
$cacheKey = 'category_' . $slug;
$collection = \cache()->remember(
$cacheKey,
900,
fn() => \App\Models\Episode::withAllTags([$slug])
->inRandomOrder()
->limit(3)
->get(),
);
$count = $collection->count();
@endphp
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded', 'tags[0]' => $slug]) }}"
class="relative mx-auto w-96 sm:w-full h-56 bg-white dark:bg-neutral-800 text-black dark:text-white rounded-lg overflow-hidden shadow-lg mt-4 transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
<h2 class="text-lg font-semibold text-center pt-2">{{ $name }}</h2>
<div class="relative w-full h-full flex justify-center">
<!-- Left Image -->
@if ($count > 0)
<img src="{{ $collection->first()->cover_url }}"
class="absolute w-32 h-44 rounded-lg object-cover shadow-md left-4 top-4 rotate-[-15deg] z-0">
@endif
<!-- Center Image -->
@if ($count > 1)
<img src="{{ $collection->skip(1)->first()->cover_url }}"
class="absolute w-32 h-44 rounded-lg object-cover shadow-lg top-4 z-10">
@endif
<!-- Right Image -->
@if ($count > 2)
<img src="{{ $collection->skip(2)->first()->cover_url }}"
class="absolute w-32 h-44 rounded-lg object-cover shadow-lg right-4 top-14 z-20 rotate-[15deg]">
@endif
</div>
</a>
@endforeach
</div>

View File

@@ -0,0 +1,61 @@
<p class="text-lg font-bold leading-normal text-neutral-800 dark:text-white">
{{ __('home.latest-comments') }}
</p>
<div class="grid grid-cols-1 gap-2 md:grid-cols-2">
@foreach ($latestComments as $comment)
@if ($comment->commentable_type == 'App\Models\Episode')
@php $episode = cache()->rememberForever('commentEpisode'.$comment->commentable_id, fn () => App\Models\Episode::with('gallery')->where('id', $comment->commentable_id)->first()); @endphp
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
<div
class="w-[20vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
<a class="hidden hover:text-blue-600 xl:block"
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ $episode->gallery->first()->thumbnail_url }}"></img>
<p
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<div class="absolute w-[95%] grid grid-cols-1 text-center">
<p class="text-sm text-center text-black dark:text-white">{{ $episode->title }} -
{{ $episode->episode }}</p>
</div>
</a>
<a class="block hover:text-blue-600 xl:hidden"
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
class="block object-cover object-center relative z-20 rounded-lg"
src="{{ $episode->cover_url }}"></img>
</a>
</div>
<div class="w-[60vw]">
@include('partials.comment', ['comment' => $comment])
</div>
</div>
@elseif($comment->commentable_type == 'App\Models\Hentai')
@php $hentai = cache()->rememberForever('commentHentai'.$comment->commentable_id, fn () => App\Models\Hentai::with('gallery', 'episodes')->where('id', $comment->commentable_id)->first()); @endphp
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
<div
class="w-[20vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
<a class="hover:text-blue-600" href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
<img alt="{{ $hentai->episodes->first()->title }}" loading="lazy" width="1000"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ $hentai->gallery->first()->thumbnail_url }}"></img>
<p
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $hentai->episodes->first()->getResolution() }}</p>
<div class="absolute w-[95%] grid grid-cols-1 text-center">
<p class="text-sm text-center text-black dark:text-white">
{{ $hentai->episodes->first()->title }}</p>
</div>
</a>
</div>
<div class="w-[60vw]">
@include('partials.comment', ['comment' => $comment])
</div>
</div>
@endif
@endforeach
</div>

View File

@@ -0,0 +1,9 @@
<p class="leading-normal font-bold text-lg text-neutral-800 dark:text-white">
Random
</p>
@php $random = \App\Models\Episode::inRandomOrder()->limit(8)->get(); @endphp
<div class="mb-6">
@include('home.partials.tab.template', ['episodes' => $random, 'showThumbnails' => false])
</div>

View File

@@ -0,0 +1,9 @@
@if ($showThumbnails)
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-2">
@include('partials.episode-thumbnail', ['limit' => 15])
</div>
@else
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-7 2xl:grid-cols-8 gap-2">
@include('partials.episode-cover', ['limit' => 16])
</div>
@endif

View File

@@ -0,0 +1,101 @@
<!--Tabs navigation-->
<ul class="-mb-6 flex list-none flex-row flex-wrap border-b-0 pl-0 relative z-10" role="tablist" data-te-nav-ref>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-most-views"
class="rounded-l-lg my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-most-views" data-te-nav-active role="tab"
aria-controls="tabs-most-views" aria-selected="true">
{{ __('home.most-views') }}
</a>
</li>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-most-likes"
class="my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-most-likes" role="tab" aria-controls="tabs-most-likes"
aria-selected="false">
{{ __('home.most-likes') }}
</a>
</li>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-popular-weekly"
class="my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-popular-weekly" role="tab"
aria-controls="tabs-popular-weekly" aria-selected="false">
{{ __('home.popular-weekly') }}
</a>
</li>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-popular-monthly"
class="rounded-r-lg my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-popular-monthly" role="tab"
aria-controls="tabs-popular-monthly" aria-selected="false">
{{ __('home.popular-monthly') }}
</a>
</li>
</ul>
@php $showThumbnails = true; @endphp
@auth
@if (!Auth::user()->home_middle_design)
@php $showThumbnails = false; @endphp
@endif
@endauth
<!--Tabs content-->
<div class="mb-6">
<div class="hidden opacity-100 transition-opacity duration-150 ease-linear data-[te-tab-active]:block"
id="tabs-most-views" role="tabpanel" aria-labelledby="tabs-most-views-tab" data-te-tab-active>
@include('home.partials.tab.template', [
'episodes' => $popularAllTime,
'showThumbnails' => $showThumbnails,
])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more-with-most-views') }}
</a>
</div>
</div>
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block"
id="tabs-most-likes" role="tabpanel" aria-labelledby="tabs-most-likes-tab">
@include('home.partials.tab.template', [
'episodes' => $mostLikes,
'showThumbnails' => $showThumbnails,
])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'view-count']) }}"
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more') }}
</a>
</div>
</div>
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block"
id="tabs-popular-weekly" role="tabpanel" aria-labelledby="tabs-popular-weekly-tab">
@include('home.partials.tab.template', [
'episodes' => $popularWeekly,
'showThumbnails' => $showThumbnails,
'popularView' => true,
])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more') }}
</a>
</div>
</div>
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block"
id="tabs-popular-monthly" role="tabpanel" aria-labelledby="tabs-popular-monthly-tab">
@include('home.partials.tab.template', [
'episodes' => $popularMonthly,
'showThumbnails' => $showThumbnails,
'popularView' => true,
])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more') }}
</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,61 @@
<!--Tabs navigation-->
<ul class="-mb-6 flex list-none flex-row flex-wrap border-b-0 pl-0 relative z-10" role="tablist" data-te-nav-ref>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-recently-uploaded" class="rounded-l-lg my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white/50 dark:bg-neutral-950/50 backdrop-blur-sm dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-recently-uploaded" data-te-nav-active role="tab" aria-controls="tabs-recently-uploaded" aria-selected="true">
{{ __('home.recently-uploaded') }} ({{ Carbon\Carbon::parse($recentlyUploaded[0]->created_at)->diffForHumans([ 'parts' => 2 ]) }})
</a>
</li>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-recently-released" class="my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white/50 dark:bg-neutral-950/50 backdrop-blur-sm dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-recently-released" role="tab" aria-controls="tabs-recently-released" aria-selected="false">
{{ __('home.recently-released') }}
</a>
</li>
<li role="presentation" class="flex-auto text-center">
<a href="#tabs-trending" class="rounded-r-lg my-2 block border-x-0 border-b-2 border-t-0 border-transparent px-7 pb-3.5 pt-4 text-xs font-medium uppercase leading-tight text-neutral-500 hover:isolate hover:border-transparent hover:bg-neutral-50 focus:isolate focus:border-transparent data-[te-nav-active]:border-rose-600 data-[te-nav-active]:text-black dark:text-neutral-400 bg-white/50 dark:bg-neutral-950/50 backdrop-blur-sm dark:hover:bg-neutral-800 dark:data-[te-nav-active]:text-white"
data-te-toggle="pill" data-te-target="#tabs-trending" role="tab" aria-controls="tabs-trending" aria-selected="false">
{{ __('home.trending') }}
</a>
</li>
</ul>
@php $showThumbnails = false; @endphp
@auth
@if(Auth::user()->home_top_design)
@php $showThumbnails = true; @endphp
@endif
@endauth
<!--Tabs content-->
<div class="mb-6">
<div class="hidden opacity-100 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-uploaded" role="tabpanel" aria-labelledby="tabs-recently-uploaded-tab" data-te-tab-active>
@include('home.partials.tab.template', ['episodes' => $recentlyUploaded, 'showThumbnails' => $showThumbnails])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'recently-uploaded']) }}"
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more-recently-uploaded') }}
</a>
</div>
</div>
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-recently-released" role="tabpanel"aria-labelledby="tabs-recently-released-tab">
@include('home.partials.tab.template', ['episodes' => $recentlyReleased, 'showThumbnails' => $showThumbnails])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
class="rounded bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more-recently-released') }}
</a>
</div>
</div>
<div class="hidden opacity-0 transition-opacity duration-150 ease-linear data-[te-tab-active]:block" id="tabs-trending" role="tabpanel"aria-labelledby="tabs-trending-tab">
@include('home.partials.tab.template', ['episodes' => $popularDaily, 'showThumbnails' => $showThumbnails, 'popularView' => true])
<div class="grid text-center pt-5 ">
<a href="{{ route('hentai.search', ['order' => 'recently-released']) }}"
class="rounded invisible bg-rose-600 p-1 mr-2 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('home.show-more') }}
</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,52 @@
<x-app-layout>
<div class="container my-24 mx-auto md:px-6 z-10 relative">
<section class="mb-32 text-center">
<div class="flex justify-center pb-10">
<img src="/images/cropped-HS-1-270x270.webp" class="max-w-[150px]" alt="hstream.moe Logo" />
</div>
<div class="grid md:grid-cols-5 lg:gap-x-12">
<div class="mb-12 md:mb-0">
<div class="mb-6 inline-block rounded-md bg-white dark:bg-neutral-950 p-4 text-sky-500">
<i class="fa-solid fa-eye text-3xl"> {{ number_format($viewCount) }}</i>
</div>
<h5 class="text-lg font-medium dark:text-neutral-300">
total views
</h5>
</div>
<div class="mb-12 md:mb-0">
<div class="mb-6 inline-block rounded-md bg-white dark:bg-neutral-950 p-4 text-sky-500">
<i class="fa-solid fa-calendar-days text-3xl"> {{ number_format($monthlyCount) }}</i>
</div>
<h5 class="text-lg font-medium dark:text-neutral-300">
views the last 30 days
</h5>
</div>
<div class="mb-12 md:mb-0">
<div class="mb-6 inline-block rounded-md bg-white dark:bg-neutral-950 p-4 text-rose-600">
<i class="fa-solid fa-video text-3xl"> {{ $episodeCount }}</i>
</div>
<h5 class="text-lg font-medium dark:text-neutral-300">
episodes on this site
</h5>
</div>
<div class="mb-12 md:mb-0">
<div class="mb-6 inline-block rounded-md bg-white dark:bg-neutral-950 p-4 text-rose-600">
<i class="fa-solid fa-list text-3xl"> {{ $hentaiCount }}</i>
</div>
<h5 class="text-lg font-medium dark:text-neutral-300">
hentais on this site
</h5>
</div>
<div class="mb-12 md:mb-0">
<div class="mb-6 inline-block rounded-md bg-white dark:bg-neutral-950 p-4 text-rose-600">
<i class="fa-solid fa-clock text-3xl"> {{ number_format($viewCount * 6) }}</i>
</div>
<h5 class="text-lg font-medium dark:text-neutral-300">
estimated minutes of watch time
</h5>
</div>
</div>
</section>
<p class="text-center text-black/40 dark:text-white/40">Cached for 60 Minutes</p>
</div>
</x-app-layout>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@include('partials.head')
<body class="font-sans antialiased">
<div class="flex flex-col min-h-screen bg-gray-100 dark:bg-neutral-900">
@include('layouts.navigation')
<!-- Page Heading -->
@if (isset($header))
<header class="bg-white dark:bg-neutral-950/50 backdrop-blur-lg shadow mt-[65px] z-10">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
@endif
<!-- Page Content -->
<div @if (!isset($header)) class="mt-[65px]" @else class="mt-[40px]" @endif>
<main>
{{ $slot }}
</main>
</div>
@include('layouts.footer')
</div>
@livewireScripts
</body>
</html>

View File

@@ -0,0 +1,67 @@
<footer class="mt-auto bg-white z-10 rounded-lg shadow dark:bg-neutral-950 m-4">
<div class="w-full xl:max-w-[95%] 2xl:max-w-[84%] mx-auto p-4">
<div class="sm:flex sm:items-center sm:justify-between">
<a href="https://hstream.moe/" class="flex items-center mb-4 sm:mb-0">
<img src="/images/cropped-HS-1-192x192.webp" class="h-8 mr-3" alt="hstream.moe Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">hstream.moe</span>
</a>
<ul class="flex flex-wrap items-center mb-6 text-sm font-medium text-gray-500 sm:mb-0 dark:text-gray-400">
<li>
<a href="{{ route('contact.index') }}" class="mr-4 hover:underline md:mr-6 "><i
class="fa-solid fa-message"></i> Contact</a>
</li>
<li>
<a href="{{ config('discord.invite_link') }}" class="mr-4 hover:underline md:mr-6 "><i
class="fa-brands fa-discord"></i> Discord</a>
</li>
<li>
<a href="{{ route('home.stats') }}" class="mr-4 hover:underline md:mr-6 "><i
class="fa-solid fa-chart-simple"></i> Stats</a>
</li>
</ul>
<!-- Links to friendly sites -->
<ul class="flex flex-wrap items-center mb-6 text-sm font-medium text-gray-500 sm:mb-0 dark:text-gray-400">
<li>
<a target="_blank" href="https://everythingmoe.com/"
class="mr-4 hover:underline md:mr-6">everythingmoe.com</a>
</li>
<li>
<a target="_blank" href="https://theindex.moe/"
class="mr-4 hover:underline md:mr-6">theindex.moe</a>
</li>
<li>
<a target="_blank" href="https://www.squid-board.org/"
class="mr-4 hover:underline md:mr-6">squidboard.org</a>
</li>
<li>
<a target="_blank" href="https://hentaizilla.com/"
class="hover:underline md:mr-6">hentaizilla.com</a>
</li>
<li>
<a target="_blank" href="https://hentaipulse.com/"
class="hover:underline md:mr-6">hentaipulse.com</a>
</li>
<li>
<a target="_blank" href="https://hentaisites.com/"
class="hover:underline md:mr-6">hentaisites.com</a>
</li>
</ul>
<ul class="flex flex-wrap items-center mb-6 text-sm font-medium text-gray-500 sm:mb-0 dark:text-gray-400">
<li>
<a class="hover:underline md:mr-6 cursor-pointer" data-te-toggle="modal"
data-te-target="#modalLanguage">Language</a>
</li>
</ul>
@if (!Session::has('alert.config'))
<script src="{{ asset('vendor/sweetalert/sweetalert.all.js') }}"></script>
@endif
@if (config('sweetalert.theme') != 'default')
<link href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-{{ config('sweetalert.theme') }}"
rel="stylesheet">
@endif
@include('sweetalert::alert')
</div>
</div>
</footer>
@include('modals.language-selector')

View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@include('partials.head')
<body class="font-sans text-gray-900 antialiased">
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
<div>
<a href="/">
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
</a>
</div>
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
{{ $slot }}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,309 @@
<nav x-data="{ open: false }"
class="bg-white/30 dark:bg-neutral-950/40 border-b border-gray-200 dark:border-neutral-700 fixed xs:absolute sm:fixed w-[100%] z-50 backdrop-blur">
<!-- Primary Navigation Menu -->
<div class="max-w-[100%] xl:max-w-[95%] 2xl:max-w-[84%] mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button
class="inline-flex items-center px-3 py-2 border text-sm leading-4 font-medium rounded-md text-gray-700 border-neutral-300/50 dark:text-gray-200 bg-white/20 dark:bg-neutral-950/20 dark:border-neutral-800/50 hover:text-gray-800 dark:hover:text-gray-100 focus:outline-none transition ease-in-out duration-150">
<div class="shrink-0 flex items-center">
<img src="/images/cropped-HS-1-192x192.webp" class="h-8 mr-3" alt="hstream.moe Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap">hstream.moe</span>
</div>
<div class="ml-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
<x-dropdown-link :href="route('home.index')">
<i class="fa-solid fa-house"></i> {{ __('nav.home') }}
</x-dropdown-link>
<x-dropdown-link :href="route('hentai.search')">
<i class="fa-solid fa-magnifying-glass"></i> {{ __('nav.search') }}
</x-dropdown-link>
<x-dropdown-link :href="route('playlist.index')">
<i class="fa-solid fa-rectangle-list"></i> {{ __('nav.public-playlists') }}
</x-dropdown-link>
@auth
<x-dropdown-link :href="route('download.search')">
<i class="fa-solid fa-download"></i> {{ __('nav.downloads') }}
</x-dropdown-link>
@endauth
<x-dropdown-link :href="config('discord.invite_link')">
<i class="fa-brands fa-discord"></i> {{ __('nav.our-discord-server') }}
</x-dropdown-link>
<x-dropdown-link>
<div class="grid grid-cols-2">
<p class="cursor-default">{{ __('nav.theme') }}</p>
<div class="flex items-center">
<div class="absolute right-6">
@include('partials.themeswitcher')
</div>
</div>
</div>
</x-dropdown-link>
</x-slot>
</x-dropdown>
</div>
<div class="flex items-center invisible sm:visible">
@livewire('nav-live-search')
<div class="hidden lg:block pl-4">
<div class="flex flex-col items-center bg-gray-50/20 dark:bg-neutral-900/40 rounded-md">
@php $random = App\Models\Episode::inRandomOrder()->limit(1)->pluck('slug')->first(); @endphp
<a href="{{ route('hentai.index', ['title' => $random]) }}"
class="cursor-pointer px-4 py-2 text-sm font-medium dark:hover:text-white text-gray-500 dark:text-white/90 focus:outline-none flex flex-col items-center md:flex-row">
<i class="fa-solid fa-shuffle"></i>
<p class="md:pl-1 pl-0">Random</p>
</a>
</div>
</div>
</div>
@auth
@php $notAvailable = Auth::user()->unreadNotifications()->count() > 0; @endphp
@else
@php $notAvailable = false; @endphp
@endauth
<!-- Settings Dropdown -->
<div class="hidden sm:flex sm:items-center sm:ml-6">
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button
class="inline-flex items-center px-3 py-2 border text-sm leading-4 font-medium rounded-md text-gray-500 border-neutral-300/50 dark:text-gray-400 bg-white/20 dark:bg-neutral-950/20 dark:border-neutral-800/50 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
@auth
@if (Auth::user()->avatar)
<img class="h-8 w-8 rounded-full object-cover mr-2"
src="https://external-content.duckduckgo.com/iu/?u=https://cdn.discordapp.com/avatars/{{ Auth::user()->id }}/{{ Auth::user()->avatar }}.webp"
alt="{{ Auth::user()->getTagAttribute() }}" />
@endif
@else
<img class="h-8 w-8 rounded-full object-cover mr-2" src="/images/default-avatar.webp"
alt="Guest" />
@endauth
@auth
<div style="display: flex; flex-direction: row; align-items: flex-start;">
{{ Auth::user()->getTagAttribute() }}
@if ($notAvailable)
<i class="fa-solid fa-bell text-rose-600"></i>
@endif
</div>
@else
<div style="display: flex; flex-direction: column; align-items: flex-start;">
Guest
<small>{{ __('nav.please-login') }}</small>
</div>
@endauth
<div class="ml-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
@auth
<x-dropdown-link :href="route('profile.show')">
<i class="fa-solid fa-user"></i> {{ __('Profile') }}
</x-dropdown-link>
@if ($notAvailable)
<x-dropdown-link :href="route('profile.notifications')">
<i class="fa-solid fa-bell animate-bounce text-rose-600"></i> Notifications
</x-dropdown-link>
@else
<x-dropdown-link :href="route('profile.notifications')">
<i class="fa-solid fa-bell"></i> Notifications
</x-dropdown-link>
@endif
<x-dropdown-link :href="route('profile.comments')">
<i class="fa-solid fa-comment"></i> {{ __('nav.comments') }}
</x-dropdown-link>
<x-dropdown-link :href="route('profile.likes')">
<i class="fa-solid fa-heart"></i> {{ __('nav.likes') }}
</x-dropdown-link>
<x-dropdown-link :href="route('profile.playlists')">
<i class="fa-solid fa-rectangle-list"></i> {{ __('nav.playlists') }}
</x-dropdown-link>
<x-dropdown-link :href="route('user.watched')">
<i class="fa-solid fa-eye"></i> {{ __('nav.watched') }}
</x-dropdown-link>
<x-dropdown-link :href="route('profile.settings')">
<i class="fa-solid fa-gear"></i> {{ __('nav.settings') }}
</x-dropdown-link>
@if (Auth::user()->is_admin)
<x-dropdown-link href="{{ route('admin.upload.index') }}">
<i class="fa-solid fa-user-tie"></i> Admin
</x-dropdown-link>
@endif
@endauth
<!-- Authentication -->
@auth
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-dropdown-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
<i class="fa-solid fa-right-from-bracket"></i> {{ __('nav.logout') }}
</x-dropdown-link>
</form>
@endauth
@guest
<x-dropdown-link :href="route('login')">
<div
class="relative bg-blue-700 hover:bg-blue-600 text-white font-bold px-4 h-10 rounded text-center p-[10px]">
<i class="fa-brands fa-discord"></i> {{ __('nav.login') }}
</div>
</x-dropdown-link>
@endguest
</x-slot>
</x-dropdown>
</div>
<!-- Hamburger -->
<div class="-mr-2 flex items-center sm:hidden">
<button @click="open = ! open"
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-neutral-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-neutral-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path :class="{ 'hidden': open, 'inline-flex': !open }" class="inline-flex"
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16" />
<path :class="{ 'hidden': !open, 'inline-flex': open }" class="hidden" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
@if ($notAvailable)
<span class="absolute mb-4 ml-4 flex h-3 w-3 float-right">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
</span>
@endif
</button>
</div>
</div>
</div>
<!-- Responsive Navigation Menu -->
@auth
<div :class="{ 'block': open, 'hidden': !open }" class="hidden sm:hidden">
<div class="pt-2 pb-3 space-y-1">
@include('partials.mobilesearch')
</div>
<!-- Responsive Settings Options -->
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600 dark:bg-neutral-900/30">
<div class="flex justify-center">
@if (Auth::user()->avatar)
<img class="h-8 w-8 rounded-full object-cover mr-2"
src="https://external-content.duckduckgo.com/iu/?u=https://cdn.discordapp.com/avatars/{{ Auth::user()->id }}/{{ Auth::user()->avatar }}.webp"
alt="{{ Auth::user()->getTagAttribute() }}" />
@else
<img class="h-8 w-8 rounded-full object-cover mr-2" src="/images/default-avatar.webp"
alt="Guest" />
@endif
<span class="font-medium text-base text-gray-800 dark:text-neutral-200">{{ Auth::user()->username }}
</span>
</div>
<div class="mt-3 space-y-1">
<x-responsive-nav-link :href="route('profile.show')" :active="request()->routeIs('user.show')">
<i class="fa-solid fa-user pr-4"></i> {{ __('nav.profile') }}
</x-responsive-nav-link>
@if ($notAvailable)
<x-responsive-nav-link :href="route('profile.notifications')" :active="request()->routeIs('profile.notifications')">
<i class="fa-solid fa-bell pr-4"></i> Notifications
<span class="relative flex h-3 w-3 float-right top-[6px]">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
</span>
</x-responsive-nav-link>
@else
<x-responsive-nav-link :href="route('profile.notifications')" :active="request()->routeIs('profile.notifications')">
<i class="fa-solid fa-bell pr-4"></i> Notifications
</x-responsive-nav-link>
@endif
<x-responsive-nav-link :href="route('profile.comments')" :active="request()->routeIs('profile.comments')">
<i class="fa-solid fa-comment pr-4"></i> {{ __('nav.comments') }}
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('profile.likes')" :active="request()->routeIs('profile.likes')">
<i class="fa-solid fa-heart pr-4"></i> {{ __('nav.likes') }}
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('profile.playlists')" :active="request()->routeIs('profile.playlists')">
<i class="fa-solid fa-rectangle-list pr-4"></i> {{ __('nav.playlists') }}
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('user.watched')" :active="request()->routeIs('user.watched')">
<i class="fa-solid fa-eye pr-4"></i> {{ __('nav.watched') }}
</x-responsive-nav-link>
<!-- Authentication -->
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-responsive-nav-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
<i class="fa-solid fa-right-from-bracket pr-4"></i> {{ __('nav.logout') }}
</x-responsive-nav-link>
</form>
</div>
</div>
</div>
@else
<div :class="{ 'block': open, 'hidden': !open }" class="hidden sm:hidden">
<!-- Search Form Mobile -->
<div class="pt-2 pb-3 space-y-1">
@include('partials.mobilesearch')
<div class="pb-1 text-center w-full">
<x-responsive-nav-link :href="route('login')">
<div
class="relative bg-blue-700 hover:bg-blue-600 text-white font-bold px-4 h-10 rounded text-center p-[10px]">
<i class="fa-brands fa-discord"></i> {{ __('nav.login') }}
</div>
</x-responsive-nav-link>
</div>
</div>
</div>
@endauth
</nav>

View File

@@ -0,0 +1,95 @@
<div>
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]" wire:keydown.right.window="nextPage" wire:keydown.left.window="previousPage">
<div class="flex items-center justify-center">
<div class="relative overflow-x-auto rounded-lg w-3/6">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-white">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-pink-700 dark:text-neutral-200 ">
<tr>
<th scope="col" class="px-6 py-3">
Discord-ID
<input
class="w-4 h-4 ml-2 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
type="checkbox"
wire:model.live="filtered"
value="true"
>
</th>
<th scope="col" class="px-6 py-3">
Username
<input
wire:model.live.debounce.600ms="search"
type="search"
id="live-search"
class="ml-2 w-32 h-7 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
placeholder="Search..."
>
</th>
<th scope="col" class="px-6 py-3">
Patreon
<input
class="w-4 h-4 ml-2 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
type="checkbox"
wire:model.live="patreon"
value="true"
>
</th>
<th scope="col" class="px-6 py-3">
Banned
<input
class="w-4 h-4 ml-2 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
type="checkbox"
wire:model.live="banned"
value="true"
>
</th>
<th scope="col" class="px-6 py-3">
Created at
</th>
<th scope="col" class="px-6 py-3">
Updated at
</th>
<th scope="col" class="px-6 py-3">
Actions
</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr wire:key="user-{{ $user->id }}" class="bg-white border-t dark:bg-neutral-800 dark:border-pink-700">
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{{ $user->id }}
</th>
<td class="px-6 py-4">
{{ $user->global_name ?? $user->username }}
</td>
<td class="px-6 py-4">
{{ $user->is_patreon ? 'Yes' : 'No' }}
</td>
<td class="px-6 py-4">
{{ $user->is_banned ? 'Yes' : 'No' }}
</td>
<td class="px-6 py-4">
{{ $user->created_at->format('Y-m-d') }}
</td>
<td class="px-6 py-4">
{{ $user->updated_at->format('Y-m-d') }}
</td>
<td class="px-6 py-4">
<form method="POST" action="{{ route('admin.user.update') }}">
@csrf
<input type="hidden" value="{{ $user->id }}" name="id">
<input type="hidden" value="{{ $user->is_banned ? 'unban' : 'ban' }}" name="action">
<button type="submit" class="inline-block rounded bg-rose-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
{{ $user->is_banned ? 'Unban' : 'Ban' }}
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{ $users->links('pagination::tailwind') }}
</div>
</div>

View File

@@ -0,0 +1,95 @@
<div>
<!-- Table -->
<div class="relative pt-5 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<div class="flex items-center justify-center">
<div class="relative overflow-x-auto rounded-lg w-3/6">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-white">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-pink-700 dark:text-neutral-200">
<tr>
<th class="px-6 py-3">Image</th>
<th class="px-6 py-3">Data
<select wire:model.live="filter" class="ml-2 p-[2px] w-20 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="all">All</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</th>
<th class="px-6 py-3">Action</th>
</tr>
</thead>
<tbody>
@foreach($images as $image)
<tr wire:key="{{ $image->id }}" class="bg-white border-t dark:bg-neutral-800 dark:border-pink-700">
<td class="px-6 py-4 h-[100px]">
<img src="/images/background/{{ $image->id }}-640p.webp" class="rounded-lg" style="width: 200px;" />
</td>
<td class="px-6 py-4">
<form method="POST" action="{{ route('admin.background.update') }}">
@csrf
@method('put')
<input type="hidden" value="{{ $image->id }}" name="id">
<div class="flex">
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="date_start">Start Date:</label>
<input
class="block text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
type="date"
name="date_start"
id="date_start"
value="{{ \Carbon\Carbon::parse($image->date_start)->format('Y-m-d') }}"
required
>
</div>
<div class="p-4">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="date_end">End Date:</label>
<input
class="block text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
type="date"
name="date_end"
id="date_end"
value="{{ \Carbon\Carbon::parse($image->date_end)->format('Y-m-d') }}"
required
>
</div>
<div class="p-4 pt-11">
<label class="leading-tight text-gray-800 dark:text-gray-200 w-full" for="default">Default:</label>
<input
type="checkbox"
name="default"
id="default"
value="1"
@if ($image->default) checked @endif
class="w-5 h-5 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
>
</div>
<div class="h-5 pt-10">
<button type="submit" class="ml-1 inline-block rounded bg-sky-800 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-sky-700 focus:bg-sky-600">
Update
</button>
</div>
</div>
</form>
</td>
<td class="px-6 py-4 pt-[69px] flex">
<form method="POST" action="{{ route('admin.background.delete') }}">
@csrf
@method('delete')
<input type="hidden" value="{{ $image->id }}" name="id">
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
Delete
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{ $images->links('pagination::tailwind') }}
</div>
</div>

View File

@@ -0,0 +1,20 @@
<div>
<a href="{{ $downloadUrl }}" wire:click="clicked({{ $downloadId }})" download>
<button id="dl-{{ $downloadId }}" class="group rounded-md shadow {{ $background }} text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[190px] h-[65px]">
<div class="relative w-12 h-[65px] bg-white bg-opacity-20 text-white flex justify-center items-center transition-all">
<svg class="w-4 h-4 transition-all group-hover:-translate-y-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</div>
<div class="flex flex-col text-center w-full">
@if($fillNumbers)
<p class="text-lg">Episode {{ str_pad($episodeNumber, 2, '0', STR_PAD_LEFT) }}</p>
@else
<p class="text-lg">Episode {{ $episodeNumber }}</p>
@endif
<p class="text-xs">HEVC MKV {{ $fileSize ?? '' }}</p>
<p class="text-xs" id="count-{{ $downloadId }}">Downloaded {{ $downloadCount }} times</p>
</div>
</button>
</a>
</div>

View File

@@ -0,0 +1,54 @@
<div>
<p class="text-gray-800 dark:text-gray-200 text-center">
You have <code>{{ $downloadsLeft }}/{{ config('hstream.free_downloads_count') }}</code> free daily 4k downloads left.
<br>
Free 4k downloads will only be available, if enough people are subscribed to patreon!
<br>
Patreon subscribers don't have any limits!
</p>
<div class="flex flex-wrap justify-around">
@if ($granted === 0 && !$override)
<a href="#" wire:click.prevent="generate">
<button class="group rounded-md shadow bg-yellow-600 text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[190px] h-[65px]">
<div class="relative w-12 h-[65px] bg-white bg-opacity-20 text-white flex justify-center items-center transition-all">
<svg class="w-4 h-4 transition-all group-hover:-translate-y-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</div>
<div class="flex flex-col text-center w-full">
<p class="text-lg">Generate download</p>
</div>
</button>
</a>
@elseif ($granted === 1 || $override)
@php
$episode = \App\Models\Episode::where('id', $episodeId)->firstOrFail();
$download = $interpolated == true ? $episode->getDownloadByType('UHDi') : $episode->getDownloadByType('UHD');
$now = \Illuminate\Support\Carbon::now();
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
$dlpdomains = config('hstream.download_domain_4k');
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
$fillNumbers = false;
@endphp
<livewire:download-button
:download-url="$downloadURL"
:download-id="$download->id"
:download-count="$download->count"
:episode-number="$episode->episode"
:fill-numbers="$fillNumbers"
:file-size="$download->getFileSize()">
@elseif ($granted === 3)
<p class="text-red-600">
Daily download limit reached!
</p>
@elseif ($granted === 4)
<p class="text-red-600">
Download will be available 7 days after release!
</p>
@endif
</div>
</div>

View File

@@ -0,0 +1,146 @@
<div class="py-3">
<div class="mx-auto sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[80%] 2xl:max-w-[50%]">
<!-- Search Filter -->
<div class="p-4 sm:p-8 bg-white dark:bg-neutral-800 shadow sm:rounded-lg">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<!-- Title -->
<div>
<label for="live-search"
class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input wire:model.live.debounce.600ms="search" type="search" id="live-search"
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
placeholder="{{ __('search.search-hentai') }}" required>
<div class="absolute right-0 top-[11px]" wire:loading>
<svg aria-hidden="true"
class="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor" />
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill" />
</svg>
</div>
</div>
</div>
<!-- Ordering -->
<div>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-sort text-gray-500 dark:text-gray-400"></i>
</div>
<select wire:model.live="order"
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="az">A-Z</option>
<option value="za">Z-A</option>
</select>
</div>
</div>
</div>
<div class="float-right text-gray-900 dark:text-white">
<input type="checkbox" wire:model.live="withTorrents">
Show only with Torrent
</div>
</div>
</div>
@php $dldomains = config('hstream.download_domain'); @endphp
<div class="relative pt-5 mx-auto sm:px-6 lg:px-8 space-y-6 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[60%]" wire:keydown.right.window="nextPage" wire:keydown.left.window="previousPage">
<div class="flex flex-col">
<div class="overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 sm:px-6 lg:px-8">
<div class="overflow-hidden">
<!-- Desktop -->
<table class="min-w-full text-left text-sm font-light collapse md:visible">
<thead class="border-b font-medium dark:border-neutral-500 bg-white dark:bg-neutral-800 shadow sm:rounded-lg">
<tr>
<th scope="col" class="px-6 py-4">Title</th>
<th scope="col" class="px-6 py-4">Download</th>
<th scope="col" class="px-6 py-4">Torrent</th>
</tr>
</thead>
<tbody>
@foreach($hentai as $h)
@php $episode = $h->episodes->first(); @endphp
<tr class="border-b dark:border-neutral-500">
<td class="whitespace-nowrap px-6 py-4">
{{ $episode->title }}
@if ($episode->title != $episode->title_jpn)
<br>
{{ $episode->title_jpn }}
@endif
@auth
@if(Auth::user()->is_admin)
<br>
<a href="{{ route('admin.add.torrentpage', ['hentai_id' => $h->id]) }}" target="_blank">Add Torrent</a>
@endif
@endauth
</td>
<td class="whitespace-nowrap px-6 py-4">
@foreach($h->episodes as $episode)
@include('livewire.partials.download-button', ['hdl' => $episode])
@endforeach
</td>
<td class="whitespace-nowrap px-6 py-4">
@include('livewire.partials.torrent-button', ['hentai' => $h])
</td>
</tr>
@endforeach
</tbody>
</table>
<!-- Mobile -->
<table class="min-w-full text-left text-sm font-light visible md:collapse">
<thead class="font-medium">
<tr>
<th scope="col" class="px-6 py-4"></th>
</tr>
</thead>
<tbody>
@foreach($hentai as $h)
@php $episode = $h->episodes->first(); @endphp
<tr class="border-b dark:border-neutral-500">
<td class="px-2 py-4 grid grid-flow-row">
{{ $episode->title }}
@if ($episode->title != $episode->title_jpn)
<br>
{{ $episode->title_jpn }}
@endif
<br>
<div class="flex flex-row flex-wrap">
@foreach($h->episodes as $episode)
@include('livewire.partials.download-button', ['hdl' => $episode])
@endforeach
@include('livewire.partials.torrent-button', ['hentai' => $h])
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
{{ $hentai->links('pagination::tailwind') }}
</div>
</div>

View File

@@ -0,0 +1,13 @@
<div>
@if (Auth::check())
<div class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:click="like" wire:poll.90000ms="update">
@else
<div data-te-toggle="tooltip" title="Please login to like the episode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:poll.60000ms="update">
@endif
@if ($liked)
<i class="fa-solid fa-heart pr-[4px] text-rose-600"></i> {{ $likeCount }}
@else
<i class="fa-regular fa-heart pr-[4px]"></i> {{ $likeCount }}
@endif
</div>
</div>

View File

@@ -0,0 +1,28 @@
<div class="py-24">
<div class="mx-auto sm:px-6 lg:px-8 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[90%]">
@include('livewire.partials.search-filter')
</div>
<input type="hidden" id="ts_reference" value="{{ Carbon\Carbon::now()->timestamp }}" />
<div class="relative pt-5 mx-auto sm:px-6 lg:px-8 space-y-6 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]" wire:keydown.right.window="nextPage" wire:keydown.left.window="previousPage">
{{ $episodes->appends(['tags' => $selectedtags])->links('pagination::tailwind') }}
<div class="flex items-center justify-center">
<div class="flex justify-center">
@if ($view == 'thumbnail')
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-2">
@else
<div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-8 gap-2">
@endif
@forelse($episodes as $episode)
@include('livewire.partials.search-result')
@empty
<div class="col-span-full">
<p class="text-2xl w-52 pt-6">No results (╥﹏╥)</p>
</div>
@endforelse
</div>
</div>
</div>
{{ $episodes->appends(['tags' => $selectedtags])->links('pagination::tailwind') }}
</div>
@vite(['resources/js/preview.js'])
</div>

View File

@@ -0,0 +1,63 @@
<div class="flex items-center">
<form method="POST" action="{{ route('hentai.searchredirect') }}">
@csrf
<label for="live-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="absolute right-2 left-2 sm:relative sm:min-w-[200px] md:min-w-[300px] lg:min-w-[400px] xl:min-w-[500px] transition-all">
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>
</div>
<input wire:model.live.debounce.600ms="navSearch" type="search" id="live-search" name="live-search" class="block p-4 pl-10 w-full text-sm text-gray-900 rounded-lg border border-gray-300/50 bg-gray-50/20 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900/40 dark:border-neutral-600/50 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" placeholder="@if(request()->path() !== 'search'){{ __('search.search-hentai') }}@endif" required @if(request()->path() == 'search') disabled @endif>
<button type="submit" class="absolute right-2.5 bottom-2.5 px-4 py-2 text-sm font-medium text-white bg-rose-700 rounded-lg hover:bg-rose-800 disabled:bg-gray-300 disabled:hover:bg-gray-300 disabled:dark:bg-gray-500 disabled:dark:hover:bg-gray-500 focus:ring-4 focus:outline-none focus:ring-rose-300 dark:bg-rose-600 dark:hover:bg-rose-700 dark:focus:ring-rose-800" @if(request()->path() == 'search') disabled @endif>{{ __('search.search') }}</button>
</div>
</form>
@if((! $hide) && request()->path() != 'search' && request()->path() != 'download-search')
<!-- BG Blur and BG Size -->
<div class="absolute left-0 sm:top-[65px] w-[100%] h-[calc(100vh-60px)] z-40 text-gray-900 dark:text-white bg-neutral-100/80 dark:bg-neutral-900/80">
<div class="flex justify-center items-center">
<!-- Padding for Grid -->
<div class="flex justify-center w-5/6">
<div class="grid grid-cols-1 gap-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
@foreach($episodes as $episode)
<div class="relative p-1 mb-8 w-full transition duration-300 ease-in-out md:p-2 hover:-translate-y-1 hover:scale-110">
<a class="hover:text-blue-600" href="{{ route('hentai.index', ['title' => $episode->slug ]) }}">
<div class="absolute w-[95%] top-[38%] text-center z-10">
<svg aria-hidden="true" class="inline mr-2 w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
</div>
<img
alt="{{ $episode->title }} - {{ $episode->episode }}"
loading="lazy"
width="500"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ $episode->gallery->first()->thumbnail_url }}">
</img>
<p class="absolute right-4 top-4 bg-white/80 dark:bg-neutral-700/80 !text-gray-900 dark:!text-white rounded-bl-lg rounded-lg p-1 pr-2 pl-2 font-semibold text-sm">{{ $episode->getResolution() }}</p>
<p class="absolute left-4 bottom-4 bg-white/80 dark:bg-neutral-700/80 !text-gray-900 dark:!text-white rounded-bl-lg rounded-lg p-1 pr-2 pl-2 font-semibold text-sm"><i class="fa-regular fa-eye"></i> {{ $episode->view_count }} <i class="fa-regular fa-heart"></i> {{ count($episode->likes) }}</p>
<p class="absolute w-[95%] text-center text-sm">{{ $episode->title }} - {{ $episode->episode }}</p>
</a>
</div>
@endforeach
<div class="relative p-1 mb-8 w-full transition duration-300 ease-in-out md:p-2 hover:-translate-y-1 hover:scale-110">
<a class="hover:text-blue-600" href="{{ route('hentai.search', ['s' => $query]) }}">
<img
alt="gallery"
loading="lazy"
width="500"
class="block object-cover object-center rounded-lg aspect-video"
src="{{ $randomimage->thumbnail_url }}">
</img>
<p class="absolute left-2 top-2 w-[95%] h-[91.5%] bg-white/10 dark:bg-neutral-700/10 !text-gray-900 dark:!text-white rounded-bl-lg rounded-lg font-semibold p-4 pr-8 pl-8 text-center"></p>
<p class="absolute left-[20%] top-[35%] bg-white/80 dark:bg-neutral-700/80 !text-gray-900 dark:!text-white rounded-bl-lg rounded-lg font-semibold p-4 pr-8 pl-8 text-center">Advanced Search...</p>
</a>
</div>
</div>
</div>
</div>
</div>
@endif
</div>

View File

@@ -0,0 +1,13 @@
<a href="{{ $dldomains[array_rand($dldomains)] }}/{{ $hdl->getDownloadByType('FHD')->url }}">
<button class="group rounded-md shadow bg-rose-600 text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[150px]">
<div class="relative w-12 h-12 bg-white bg-opacity-20 text-white flex justify-center items-center transition-all"><svg id="arrow" class="w-4 h-4 transition-all group-hover:-translate-y-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
<div id="progress" class="absolute w-full h-0 bg-white bg-opacity-20 top-0 duration-200"></div>
</div>
<div class="w-32 text-center">
<p class="px-5 text-sm row-span-2">Episode {{ $hdl->episode }}</p>
<p class="px-5 text-xs">HEVC MKV</p>
</div>
</button>
</a>

View File

@@ -0,0 +1,123 @@
<!-- Search Filter -->
<div>
<div class="p-4 sm:p-8 bg-white/40 dark:bg-neutral-950/40 backdrop-blur shadow sm:rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
<!-- Title -->
<div>
<label for="live-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input wire:model.live.debounce.600ms="search" type="search" id="live-search" class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900" placeholder="{{ __('search.search-hentai') }}" required>
<div class="absolute right-0 top-[11px]" wire:loading>
<svg aria-hidden="true" class="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor" />
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill" />
</svg>
</div>
</div>
</div>
<!-- Genres -->
<div>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-sliders text-gray-500 dark:text-gray-400"></i>
</div>
<p data-te-toggle="modal" data-te-target="#modalGenres" data-te-ripple-init data-te-ripple-color="light" id="genres-filter" class="block cursor-pointer w-full p-4 pl-10 text-sm text-gray-500 dark:text-gray-400 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:focus:ring-rose-800 dark:focus:border-rose-900">
@if($tagcount === 0)
Select Genres
@elseif($tagcount === 1)
Selected {{$tagcount }} Genre
@elseif($tagcount > 1)
Selected {{$tagcount }} Genres
@endif
</p>
</div>
</div>
<!-- Genres Blacklist -->
<div>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-shield text-gray-500 dark:text-gray-400"></i>
</div>
<p data-te-toggle="modal" data-te-target="#modalBlacklist" data-te-ripple-init data-te-ripple-color="light" id="blacklist-filter" class="block cursor-pointer w-full p-4 pl-10 text-sm text-gray-500 dark:text-gray-400 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:focus:ring-rose-800 dark:focus:border-rose-900">
@if($blacklistcount === 0)
Select Blacklist
@elseif($blacklistcount === 1)
Selected {{ $blacklistcount }} Blacklist Item
@elseif($blacklistcount > 1)
Selected {{ $blacklistcount }} Blacklist Items
@endif
</p>
</div>
</div>
<!-- Studios -->
<div>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-microphone-lines text-gray-500 dark:text-gray-400"></i>
</div>
<p data-te-toggle="modal" data-te-target="#modalStudios" data-te-ripple-init data-te-ripple-color="light" id="studios-filter" class="block cursor-pointer w-full p-4 pl-10 text-sm text-gray-500 dark:text-gray-400 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:focus:ring-rose-800 dark:focus:border-rose-900">
@if($studiocount === 0)
Select Studios
@elseif($studiocount === 1)
Selected {{ $studiocount }} Studio
@elseif($studiocount > 1)
Selected {{ $studiocount }} Studios
@endif
</p>
</div>
</div>
<!-- Ordering -->
<div class="grid grid-cols-2">
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-sort text-gray-500 dark:text-gray-400"></i>
</div>
<select wire:model.live="order" class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="az">A-Z</option>
<option value="za">Z-A</option>
<option value="recently-uploaded">{{ __('home.recently-uploaded') }}</option>
<option value="recently-released">{{ __('home.recently-released') }}</option>
<option value="oldest-uploads">{{ __('search.oldest-uploads') }}</option>
<option value="oldest-releases">{{ __('search.oldest-releases') }}</option>
<option value="view-count">{{ __('search.view-count') }}</option>
</select>
</div>
<div class="relative right-2 left-0 ml-2 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-list text-gray-500 dark:text-gray-400"></i>
</div>
<select wire:model.live="view" class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="thumbnail">Thumbnail</option>
<option value="poster">Poster</option>
</select>
</div>
</div>
</div>
@auth
<div class="float-right pt-1">
<input class="w-4 h-4 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
type="checkbox" wire:model.live="hideWatched" value="true" id="checkBoxHideWatched" />
<label class="inline-block hover:cursor-pointer dark:text-white" for="checkBoxHideWatched">
Hide watched
</label>
</div>
@endauth
</div>
@include('modals.filter-genres')
@include('modals.filter-studios')
@include('modals.filter-blacklist')
</div>

View File

@@ -0,0 +1,86 @@
<div wire:key="episode-{{ $episode->id }}">
@if ($searchIsJpn)
<div class="relative p-1 mb-14 w-full transition duration-300 ease-in-out md:p-2 hover:-translate-y-1 hover:scale-110"
data-thumbs="{{ optional($episode->gallery)->pluck('thumbnail_url') }}">
@else
<div class="relative p-1 mb-8 w-full transition duration-300 ease-in-out md:p-2 hover:-translate-y-1 hover:scale-110"
data-thumbs="{{ optional($episode->gallery)->pluck('thumbnail_url') }}">
@endif
<a class="hover:text-blue-600" href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
<div class="absolute w-[95%] top-[38%] text-center z-10">
<svg aria-hidden="true"
class="inline mr-2 w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor" />
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill" />
</svg>
</div>
@switch(true)
@case($view === 'thumbnail')
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="500"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ optional($episode->gallery->first())->thumbnail_url }}">
@if ($episode->hasAutoTrans())
<p
class="absolute right-2 bottom-2 bg-blue-600/80 !text-white rounded-tl-lg rounded-br-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-closed-captioning"></i> Multi-Subs
</p>
@endif
@break
@case($view === 'poster')
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="400"
class="block relative rounded-lg object-cover object-center aspect-[11/16] z-20"
src="{{ $episode->cover_url }}">
@break
@endswitch
@php $problematic = cache()->rememberForever('episodeProblematic'.$episode->id, fn () => $episode->getProblematicTags()); @endphp
@if (!empty($problematic))
<p
class="absolute left-2 top-2 bg-red-700/70 !text-white rounded-br-lg rounded-tl-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-solid fa-triangle-exclamation"></i> {{ $problematic }}
</p>
@endif
@if (auth()->check() && $episode->userWatched(auth()->user()->id))
<p
class="absolute right-2 top-2 bg-green-600/80 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<p
class="absolute left-2 bottom-2 bg-green-600/80 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i>
{{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i> {{ $episode->commentCount() }}
</p>
@else
<p
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
{{ $episode->getResolution() }}</p>
<p
class="absolute left-2 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i>
{{ $episode->viewCountFormatted() }}
<i class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@endif
<div class="absolute w-[95%] grid grid-cols-1 text-center">
@if ($searchIsJpn)
<p class="text-sm text-center text-black dark:text-white">{{ $episode->title }}
({{ $episode->title_jpn }}) - {{ $episode->episode }}</p>
@else
<p class="text-sm text-center text-black dark:text-white">{{ $episode->title }} -
{{ $episode->episode }}</p>
@endif
</div>
</a>
</div>
</div>

View File

@@ -0,0 +1,25 @@
@forelse ($hentai->torrents as $torrent)
<a href="{{ $torrent->torrent_url }}" target="_blank">
<button class="group rounded-md shadow bg-primary-600 text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[150px]">
<div class="relative w-12 h-12 bg-white bg-opacity-20 text-white flex justify-center items-center transition-all">
<i id="arrow" class="fa-solid fa-magnet transition-all group-hover:-translate-y-1"></i>
<div id="progress" class="absolute w-full h-0 bg-white bg-opacity-20 top-0 duration-200"></div>
</div>
<div class="w-32 text-center">
<p class="text-sm">Episode {{ $torrent->episodes }}</p>
</div>
</button>
</a>
@empty
<a>
<button class="group rounded-md shadow bg-gray-600 text-white cursor-pointer flex justify-between items-center overflow-hidden transition-all hover:glow m-1 w-[150px]">
<div class="relative w-12 h-12 bg-white bg-opacity-20 text-white flex justify-center items-center transition-all">
<i id="arrow" class="fa-solid fa-magnet transition-all group-hover:-translate-y-1"></i>
<div id="progress" class="absolute w-full h-0 bg-white bg-opacity-20 top-0 duration-200"></div>
</div>
<div class="w-32 text-center">
<p class="text-sm">Torrent not available</p>
</div>
</button>
</a>
@endforelse

View File

@@ -0,0 +1,101 @@
<div>
<div
class="relative pt-5 mx-auto sm:px-6 lg:px-8 space-y-6 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]">
<!-- Header -->
<div class="flex text-sm font-light bg-neutral-950/50 backdrop-blur-lg rounded-lg p-10 gap-2">
<a href="{{ route('user.index', ['username' => $playlist->user->username]) }}">
@if ($playlist->user->avatar)
<img class="relative w-24 h-24 flex-none rounded-full shadow-lg"
src="https://external-content.duckduckgo.com/iu/?u=https://cdn.discordapp.com/avatars/{{ $playlist->user->id }}/{{ $playlist->user->avatar }}.webp">
@else
<img class="relative w-24 h-24 flex-none rounded-full shadow-lg" src="/images/default-avatar.webp">
@endif
</a>
<div class="flex flex-col justify-center flex-1 pl-4">
<h1 class="font-bold text-3xl">{{ $playlist->name }}</h1>
<p class="font-light text-lg text-neutral-200">Episodes: {{ count($playlistEpisodes) }}</p>
<p class="font-light text-lg text-neutral-200">Creator: <a
href="{{ route('user.index', ['username' => $playlist->user->username]) }}">{{ $playlist->user->username }}</a>
</p>
</div>
<div class="flex flex-col justify-center pl-4">
<div class="flex justify-end">
<a href="{{ route('hentai.index', ['title' => $playlistEpisodes->first()->episode->slug, 'playlist' => $playlist->id]) }}"
class="cursor-pointer float-right text-white bg-rose-700 hover:bg-rose-800 focus:ring-4 focus:outline-none focus:ring-rose-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-rose-600 dark:hover:bg-rose-700 dark:focus:ring-rose-800">{{ __('playlist.play') }}</a>
</div>
</div>
</div>
@forelse($playlistEpisodes as $playlistEpisode)
@php $episode = $playlistEpisode->episode; @endphp
<div wire:key="playlist-episode-{{ $playlistEpisode->id }}"
class="flex justify-between items-center rounded-lg hover:bg-black border border-neutral-950 bg-neutral-950/50 backdrop-blur-lg transition !mt-1 gap-2">
<div class="flex pl-5 pr-5 w-10">
{{ $playlistEpisode->position }}
</div>
<div class="flex-[2] hidden md:block">
<div class="relative p-1 w-full md:p-2">
<a href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
src="{{ $episode->gallery->first()->thumbnail_url }}">
@guest
<p
class="absolute left-2 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30 collapse md:visible">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i>
{{ $episode->likeCount() }} <i class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@endguest
@auth
@if ($episode->userWatched(auth()->user()->id))
<p
class="absolute left-2 bottom-2 bg-green-600/80 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i
class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@else
<p
class="absolute left-2 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i
class="fa-regular fa-comment"></i>
{{ $episode->commentCount() }}
</p>
@endif
@endauth
</a>
</div>
</div>
<div class="flex-[5]">
<a href="{{ route('hentai.index', ['title' => $episode->slug]) }}"
class="font-bold">{{ $episode->title }} - {{ $episode->episode }}</a>
<br>
<a href="{{ route('hentai.index', ['title' => $episode->slug]) }}">{{ $episode->title_jpn }}</a>
</div>
<div class="pr-5">
@auth
@if (Auth::user()->id === $playlist->user->id)
<button class="pr-2" wire:click="moveUp({{ $playlistEpisode->id }})"><i
class="fa-solid fa-arrow-up"></i></button>
<button class="pr-2" wire:click="moveDown({{ $playlistEpisode->id }})"><i
class="fa-solid fa-arrow-down"></i></button>
<button wire:click="remove({{ $playlistEpisode->id }})" class="text-red-500"><i
class="fa-solid fa-trash"></i></button>
@endif
@endauth
</div>
</div>
@empty
<div class="pt-6 text-2xl text-center">
No results (╥﹏╥)
</div>
@endforelse
</div>
</div>

View File

@@ -0,0 +1,83 @@
<div>
<!-- Search -->
<div class="p-4 mx-3 sm:p-8 bg-white/40 dark:bg-neutral-950/40 backdrop-blur shadow sm:rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="live-search"
class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input wire:model.live.debounce.600ms="search" type="search" id="playlist-search"
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
placeholder="Search Playlist...">
<div class="absolute right-0 top-[11px]" wire:loading>
<svg aria-hidden="true"
class="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor" />
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill" />
</svg>
</div>
</div>
</div>
<!-- Ordering -->
<div class="relative right-2 left-0 sm:left-2 transition-all">
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
<i class="fa-solid fa-sort text-gray-500 dark:text-gray-400"></i>
</div>
<select wire:model.live="order"
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="az">A-Z</option>
<option value="za">Z-A</option>
<option value="episode-count">Episode count</option>
<option value="newest">Newest</option>
<option value="oldest">Oldest</option>
</select>
</div>
</div>
</div>
<div class="grid-cols-1 sm:grid md:grid-cols-3" wire:keydown.right.window="nextPage"
wire:keydown.left.window="previousPage">
@foreach ($playlists as $playlist)
<div wire:key="playlist-{{ $playlist->id }}"
class="mx-3 mt-6 flex flex-col rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-950/50 backdrop-blur-lg sm:shrink-0 sm:grow sm:basis-0 z-10">
<a href="{{ route('playlist.show', $playlist->id) }}">
@php
$pe = \App\Models\PlaylistEpisode::where('playlist_id', $playlist->id)
->orderBy('position', 'desc')
->first();
@endphp
<img class="rounded-t-lg aspect-video" src="{{ $pe->episode->gallery->first()->thumbnail_url }}"
alt="Hollywood Sign on The Hill" />
</a>
<div class="p-6">
<h5 class="mb-2 text-xl font-medium leading-tight text-neutral-800 dark:text-neutral-50">
{{ $playlist->name }}
</h5>
<p class="mb-2 text-sm leading-tight text-neutral-800 dark:text-neutral-50">
{{ $playlist->episodes_count }} {{ __('home.episodes') }}
<a href="{{ route('hentai.index', ['title' => $playlist->episodes->first()->episode->slug, 'playlist' => $playlist->id]) }}"
class="cursor-pointer float-right text-white bg-rose-700 hover:bg-rose-800 focus:ring-4 focus:outline-none focus:ring-rose-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-rose-600 dark:hover:bg-rose-700 dark:focus:ring-rose-800">{{ __('playlist.play') }}</a>
</p>
</div>
</div>
@endforeach
</div>
<div class="mt-10 mb-10">
{{ $playlists->links('pagination::tailwind') }}
</div>
</div>

View File

@@ -0,0 +1,28 @@
<div>
<div class="md:ml-8 my-8 md:my-0 space-y-6 max-w-[100%] xl:max-w-[95%] 2xl:max-w-[95%]">
@include('livewire.partials.search-filter')
</div>
<input type="hidden" id="ts_reference" value="{{ Carbon\Carbon::now()->timestamp }}" />
<div class="relative md:ml-8 pt-5 mx-auto space-y-6 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[95%]" wire:keydown.right.window="nextPage" wire:keydown.left.window="previousPage">
{{ $episodes->appends(['tags' => $selectedtags])->links('pagination::tailwind') }}
<div class="flex items-center justify-center">
<div class="flex justify-center">
@if ($view == 'thumbnail')
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4 gap-2">
@else
<div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-8 gap-2">
@endif
@forelse($episodes as $episode)
@include('livewire.partials.search-result')
@empty
<div class="col-span-full">
<p class="text-2xl w-52 pt-6">No results (╥﹏╥)</p>
</div>
@endforelse
</div>
</div>
</div>
{{ $episodes->appends(['tags' => $selectedtags])->links('pagination::tailwind') }}
</div>
@vite(['resources/js/preview.js'])
</div

View File

@@ -0,0 +1,5 @@
<div>
<a class="text-xl text-gray-800 dark:text-gray-200 leading-tight whitespace-nowrap" wire:poll.90000ms="update">
<i class="fa-regular fa-eye pr-0.5"></i> {{ $viewCount }}
</a>
</div>

View File

@@ -0,0 +1,28 @@
<div>
<div class="relative mx-auto sm:px-6 lg:px-8 text-gray-900 dark:text-white xl:max-w-[95%] 2xl:max-w-[90%]"
wire:keydown.right.window="nextPage" wire:keydown.left.window="previousPage">
<ol class="border-l border-neutral-300 dark:border-neutral-500">
@foreach ($watchedGrouped as $day => $episodes)
<li>
<div class="flex items-center pt-3 flex-start">
<div class="-ml-[5px] mr-3 h-[9px] w-[9px] rounded-full bg-neutral-300 dark:bg-neutral-500">
</div>
<p class="text-sm text-neutral-500 dark:text-neutral-300">
{{ $episodes->first()->created_at->diffForHumans(['parts' => 1]) }}
</p>
</div>
<div class="flex justify-center">
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
@foreach ($episodes as $episode)
<div class="mt-2 mb-6 ml-4">
<x-episode-thumbnail :episode="$episode->episode" />
</div>
@endforeach
</div>
</div>
</li>
@endforeach
</ol>
{{ $watched->links('pagination::tailwind') }}
</div>
</div

View File

@@ -0,0 +1,98 @@
@auth
<!--Verically centered modal-->
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalAddToPlaylist" tabindex="-1" aria-labelledby="Playlist" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[30%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Add to Playlist')" />
<!--Modal body-->
<div class="relative p-4">
<!-- Add to existing playlist -->
@php $playlists = Auth::user()->playlists; @endphp
@if (count($playlists) > 0)
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="playlist">Select Playlist:</label>
<select name="playlist" id="playlist" class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
@foreach($playlists as $playlist)
<option value="{{ $playlist->id }}">
{{ $playlist->name }} -
{{ $playlist->is_private == 1 ? 'Private' : 'Public' }} -
{{ $playlist->episodes->count() }} Episodes
{{ $playlist->episodes->contains('episode_id', $episode->id) ? '- Episode Already Added' : '' }}
</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('playlist')" class="mt-2" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<a id="playlist-cancel" class="inline-block cursor-pointer rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</a>
<a id="playlist-add" class="ml-1 cursor-pointer inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Add
</a>
</div>
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Enter Playlist Name Here:</label>
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" required autofocus/>
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="visiblity">Visiblity:</label>
<select name="visiblity" id="visiblity" class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="public">Public</option>
<option value="private" selected>Private</option>
</select>
<x-input-error :messages="$errors->get('visiblity')" class="mt-2" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<a id="playlist-create-and-add" class="ml-1 cursor-pointer inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create and Add Episode
</a>
</div>
@else
<!-- Create Playlist -->
<a class="font-semibold text-gray-800 dark:text-gray-200 leading-tight">
No Playlists found. Please create a Playlist first!
</a>
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Enter Playlist Name Here:</label>
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" required autofocus/>
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<div class="mt-5 p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="visiblity">Visiblity:</label>
<select name="visiblity" id="visiblity" class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="public">Public</option>
<option value="private" selected>Private</option>
</select>
<x-input-error :messages="$errors->get('visiblity')" class="mt-2" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<a id="playlist-cancel" class="inline-block cursor-pointer rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</a>
<a id="playlist-create-and-add" class="ml-1 cursor-pointer inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create and Add Episode
</a>
</div>
@endif
</div>
</div>
</div>
</div>
@vite(['resources/js/modals-playlist.js'])
@endauth

View File

@@ -0,0 +1,50 @@
<!--Verically centered modal-->
<div
data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="comment-modal-{{ $comment->getKey() }}"
tabindex="-1"
aria-labelledby="exampleModalCenterTitle"
aria-modal="true"
role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[40%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('comments::comments.edit_comment')" />
<!--Modal body-->
<div class="relative p-4">
<form method="POST" action="{{ route('comments.update', $comment->getKey()) }}">
@method('PUT')
@csrf
<div class="modal-body">
<div class="form-group">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="message">@lang('comments::comments.update_your_message_here')</label>
<textarea class="peer block min-h-[auto] w-full border-1 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear dark:placeholder:text-neutral-200 border-gray-300 dark:border-neutral-950 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm" name="message" rows="3">{{ $comment->comment }}</textarea>
</div>
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button
type="button"
id="modal-blacklist-filter-close-bottom"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
data-te-modal-dismiss
data-te-ripple-init
data-te-ripple-color="light">
@lang('comments::comments.cancel')
</button>
<button
type="submit"
id="modal-blacklist-filter-save"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600"
data-te-modal-dismiss
data-te-ripple-init
data-te-ripple-color="light">
@lang('comments::comments.update')
</button>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<!--Verically centered modal-->
<div
data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="reply-modal-{{ $comment->getKey() }}"
tabindex="-1"
aria-labelledby="exampleModalCenterTitle"
aria-modal="true"
role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[40%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('comments::comments.reply_to_comment')" />
<!--Modal body-->
<div class="relative p-4">
<form method="POST" action="{{ route('comments.reply', $comment->getKey()) }}">
@csrf
<div class="modal-body">
<div class="form-group">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="message">@lang('comments::comments.enter_your_message_here')</label>
<textarea required class="peer block min-h-[auto] w-full border-1 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear dark:placeholder:text-neutral-200 border-gray-300 dark:border-neutral-950 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm" name="message" rows="3"></textarea>
</div>
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button
type="button"
id="modal-blacklist-filter-close-bottom"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
data-te-modal-dismiss
data-te-ripple-init
data-te-ripple-color="light">
@lang('comments::comments.cancel')
</button>
<button
type="submit"
id="modal-blacklist-filter-save"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600"
data-te-modal-dismiss
data-te-ripple-init
data-te-ripple-color="light">
@lang('comments::comments.reply')
</button>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,39 @@
<!--Verically centered modal-->
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalCreatePlaylist" tabindex="-1" aria-labelledby="Playlist" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[30%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Create Playlist')" />
<!--Modal body-->
<div class="relative p-4">
<form method="POST" action="{{ route('profile.playlists.create') }}">
@csrf
<div class="p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="name">Enter Playlist Name Here:</label>
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" required autofocus/>
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<div class="mt-5 p-4">
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="visiblity">Visiblity:</label>
<select name="visiblity" id="visiblity" class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="public">Public</option>
<option value="private" selected>Private</option>
</select>
<x-input-error :messages="$errors->get('visiblity')" class="mt-2" />
</div>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="button" class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200" data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</button>
<button type="submit" class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600" data-te-ripple-init data-te-ripple-color="light">
Create
</button>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,19 @@
<!--Verically centered modal-->
<div data-te-modal-init class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none" id="modalDownload" tabindex="-1" aria-labelledby="Download" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[50%] 2xl:min-[576px]:max-w-[30%]">
<div class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title='__("Download {$episode->title} - {$episode->episode}")' />
@php
$dldomains = config('hstream.download_domain');
$dlDomainsBackup = config('hstream.asia_download_domain');
@endphp
<!--Modal body-->
<div class="relative p-4">
@include('modals.partials.download-guest')
@include('modals.partials.download-authorized')
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,165 @@
<!--Verically centered modal-->
<div data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="modalBlacklist" tabindex="-1" aria-labelledby="modalBlacklist" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref
class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[60%]">
<div
class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Blacklist')" />
<!--Modal body-->
<div class="relative p-4">
@php
$taglist = \cache()->remember(
'searchtags',
300,
fn() => Conner\Tagging\Model\Tag::where('count', '>', 0)->orderBy('slug', 'ASC')->get(),
);
$appearances = [
'Loli',
'Shota',
'Milf',
'Futanari',
'Big Boobs',
'Small Boobs',
'Dark Skin',
'Cosplay',
'Elf',
'Maid',
'Nekomimi',
'Nurse',
'School Girl',
'Succubus',
'Teacher',
'Trap',
'Pregnant',
'Glasses',
'Swim Suit',
'Ugly Bastard',
'Monster',
];
$types = [
'3D',
'4K',
'48Fps',
'4K 48Fps',
'Censored',
'Uncensored',
'Comedy',
'Fantasy',
'Horror',
'Vanilla',
'Ntr',
'Pov',
'Filmed',
'X-Ray',
];
$actions = [
'Anal',
'Bdsm',
'Facial',
'Blow Job',
'Boob Job',
'Foot Job',
'Hand Job',
'Rimjob',
'Inflation',
'Masturbation',
'Public Sex',
'Rape',
'Reverse Rape',
'Threesome',
'Orgy',
'Gangbang',
];
@endphp
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($taglist as $tag)
@if (in_array($tag->name, $types) || in_array($tag->name, $appearances) || in_array($tag->name, $actions))
@continue
@endif
<li class="inline-block m-1">
<input class="m-5 hidden peer" wire:model="blacklist" type="checkbox"
id="blacklist-{{ $tag->slug }}" name="blacklist[]" value="{{ $tag->slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="blacklist-{{ $tag->slug }}">{{ $tag->name }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Actions -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Action
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($actions as $tag)
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="blacklist" type="checkbox"
id="blacklist-{{ $slug }}" name="blacklist[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="blacklist-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Character Appearance -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Appearance
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($appearances as $tag)
@guest
@php if ($tag === "Loli" || $tag === "Shota") continue; @endphp
@endguest
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="blacklist" type="checkbox"
id="blacklist-{{ $slug }}" name="blacklist[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="blacklist-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Video Types -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Type
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($types as $tag)
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="blacklist" type="checkbox"
id="blacklist-{{ $slug }}" name="blacklist[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="blacklist-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
</div>
<!--Modal footer-->
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button data-te-modal-dismiss wire:click="revertFilters" type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200">
Close
</button>
<button data-te-modal-dismiss wire:click="applyFilters" type="button"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
Apply
</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,166 @@
<!--Verically centered modal-->
<div data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="modalGenres" tabindex="-1" aria-labelledby="modalGenres" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref
class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[60%]">
<div
class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Genres')" />
<form>
<!--Modal body-->
<div class="relative p-4">
@php
$taglist = cache()->remember(
'searchtags',
300,
fn() => Conner\Tagging\Model\Tag::where('count', '>', 0)->orderBy('slug', 'ASC')->get(),
);
$appearances = [
'Loli',
'Shota',
'Milf',
'Futanari',
'Big Boobs',
'Small Boobs',
'Dark Skin',
'Cosplay',
'Elf',
'Maid',
'Nekomimi',
'Nurse',
'School Girl',
'Succubus',
'Teacher',
'Trap',
'Pregnant',
'Glasses',
'Swim Suit',
'Ugly Bastard',
'Monster',
];
$types = [
'3D',
'4K',
'48Fps',
'4K 48Fps',
'Censored',
'Uncensored',
'Comedy',
'Fantasy',
'Horror',
'Vanilla',
'Ntr',
'Pov',
'Filmed',
'X-Ray',
];
$actions = [
'Anal',
'Bdsm',
'Facial',
'Blow Job',
'Boob Job',
'Foot Job',
'Hand Job',
'Rimjob',
'Inflation',
'Masturbation',
'Public Sex',
'Rape',
'Reverse Rape',
'Threesome',
'Orgy',
'Gangbang',
];
@endphp
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($taglist as $tag)
@if (in_array($tag->name, $types) || in_array($tag->name, $appearances) || in_array($tag->name, $actions))
@continue
@endif
<li class="inline-block m-1">
<input class="m-5 hidden peer" wire:model="tags" type="checkbox"
id="tags-{{ $tag->slug }}" name="tags[]" value="{{ $tag->slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="tags-{{ $tag->slug }}">{{ $tag->name }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Actions -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Action
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($actions as $tag)
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="tags" type="checkbox"
id="tags-{{ $slug }}" name="tags[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="tags-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Character Appearance -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Appearance
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($appearances as $tag)
@guest
@php if ($tag === "Loli" || $tag === "Shota") continue; @endphp
@endguest
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="tags" type="checkbox"
id="tags-{{ $slug }}" name="tags[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="tags-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
<br>
<!-- Video Types -->
<p class="font-medium leading-normal text-neutral-800 dark:text-white">
Type
</p>
<ul class="list-none text-justify" style="overflow: hidden;">
@foreach ($types as $tag)
<li class="inline-block m-1">
@php $slug = Illuminate\Support\Str::slug($tag); @endphp
<input class="m-5 hidden peer" wire:model="tags" type="checkbox"
id="tags-{{ $slug }}" name="tags[]" value="{{ $slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="tags-{{ $slug }}">{{ $tag }}</label>
</li>
@endforeach
</ul>
</div>
<!--Modal footer-->
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button data-te-modal-dismiss wire:click="revertFilters" type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200">
Close
</button>
<button data-te-modal-dismiss wire:click="applyFilters" type="button"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
Apply
</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,40 @@
<!--Verically centered modal-->
<div data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="modalStudios" tabindex="-1" aria-labelledby="modalStudios" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref
class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[95%] md:min-[576px]:max-w-[90%] lg:min-[576px]:max-w-[80%] xl:min-[576px]:max-w-[70%] 2xl:min-[576px]:max-w-[60%]">
<div
class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Studios')" />
<!--Modal body-->
<div class="relative p-4">
<ul class="list-none text-justify" style="overflow: hidden;">
@php $studios = \cache()->remember('searchstudios', 300, fn () => App\Models\Studios::orderBy('name', 'ASC')->get()); @endphp
@foreach ($studios as $studio)
<li class="inline-block m-1">
<input class="m-5 hidden peer" wire:model="studios" type="checkbox"
id="studio-{{ $studio->slug }}" name="studios[]" value="{{ $studio->slug }}">
<label
class="relative block cursor-pointer p-2 rounded bg-neutral-200 dark:bg-neutral-600 peer-checked:bg-rose-600 text-black peer-checked:text-white dark:peer-checked:text-white dark:text-white select-none"
for="studio-{{ $studio->slug }}">{{ $studio->name }}</label>
</li>
@endforeach
</ul>
</div>
<!--Modal footer-->
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button data-te-modal-dismiss wire:click="revertFilters" type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200">
Close
</button>
<button data-te-modal-dismiss wire:click="applyFilters" type="button"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
Apply
</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,47 @@
<!--Verically centered modal-->
<div data-te-modal-init
class="fixed left-0 top-0 z-[1055] hidden h-full w-full overflow-y-auto overflow-x-hidden outline-none"
id="modalLanguage" tabindex="-1" aria-modal="true" role="dialog">
<div data-te-modal-dialog-ref
class="pointer-events-none relative flex min-h-[calc(100%-1rem)] w-auto translate-y-[-50px] items-center opacity-0 transition-all duration-300 ease-in-out min-[576px]:mx-auto min-[576px]:mt-7 min-[576px]:min-h-[calc(100%-3.5rem)] min-[576px]:max-w-[90%] md:min-[576px]:max-w-[80%] lg:min-[576px]:max-w-[60%] xl:min-[576px]:max-w-[40%] 2xl:min-[576px]:max-w-[20%]">
<div
class="pointer-events-auto relative flex w-full flex-col rounded-md border-none bg-white bg-clip-padding text-current shadow-lg outline-none dark:bg-neutral-800">
<x-modal-header :title="__('Language')" />
<!--Modal body-->
<div class="relative p-4">
<form method="POST" action="{{ route('update.language') }}">
@csrf
<label class="mb-2 leading-tight text-gray-800 dark:text-gray-200 w-full" for="language">Select
Language:</label>
<select name="language" id="language"
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
<option value="en" @if ('en' == App::getLocale()) selected @endif>
English (en)
</option>
<option value="de" @if ('de' == App::getLocale()) selected @endif>
Deutsch (de)
</option>
<option value="fr" @if ('fr' == App::getLocale()) selected @endif>
Français (fr)
</option>
</select>
<div class="flex flex-shrink-0 flex-wrap items-center justify-end rounded-b-md p-4">
<button type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
data-te-modal-dismiss data-te-ripple-init data-te-ripple-color="light">
Cancel
</button>
<button type="submit"
class="ml-1 inline-block rounded bg-rose-600 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600"
data-te-ripple-init data-te-ripple-color="light">
Apply
</button>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,98 @@
@auth
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-green-400"></i> 1080p
</p>
<div class="flex flex-wrap justify-around">
@php
$dlList = App\Models\Episode::where('hentai_id', $episode->hentai_id)
->orderBy('episode', 'asc')
->get();
$fillNumbers = $dlList->count() >= 10;
@endphp
@foreach ($dlList as $hdl)
@include('modals.partials.download-button')
@endforeach
@include('livewire.partials.torrent-button', ['hentai' => $episode->hentai])
</div>
@include('modals.partials.download-backup')
@if ($episode->interpolated)
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-green-400"></i> 1080p 48fps
</p>
<div class="flex flex-wrap justify-around">
@foreach ($dlList as $hdl)
@include('modals.partials.download-button-interpolated')
@endforeach
</div>
@endif
<br>
@if (!Auth::user()->is_patreon)
@if (config('hstream.free_downloads'))
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k
</p>
@livewire('downloads-free', ['episode' => $episode])
@else
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock pr-[4px] text-red-600"></i> 4k
</p>
<p class="text-gray-800 dark:text-gray-200">
4k Downloads are restricted to patreon subscribers. If you are subscribed to patreon and you have a patreon role
on our Discord server, you have to logout and login again.
</p>
@endif
@else
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-green-400"></i> 4k
</p>
@php $dlpdomains = config('hstream.download_domain_4k'); @endphp
<div class="flex flex-wrap justify-around">
@foreach ($dlList as $hdl)
@include('modals.partials.download-button-patreon')
@endforeach
</div>
@endif
<br>
@if ($episode->interpolated_uhd)
@if (!Auth::user()->is_patreon)
@if (config('hstream.free_downloads'))
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k 48fps
</p>
@livewire('downloads-free', ['episode' => $episode, 'interpolated' => true])
@else
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock pr-[4px] text-red-600"></i> 4k 48fps
</p>
<p class="text-gray-800 dark:text-gray-200">
4k 48fps Downloads are restricted to patreon subscribers. If you are subscribed to patreon and you have a patreon role
on our Discord server, you have to logout and login again.
</p>
@endif
@else
<p class="font-bold text-gray-800 dark:text-gray-200">
<i class="fa-solid fa-lock-open pr-[4px] text-green-400"></i> 4k 48fps
</p>
@php $dlpdomains = config('hstream.download_domain_4k'); @endphp
<div class="flex flex-wrap justify-around">
@foreach ($dlList as $hdl)
@include('modals.partials.download-button-patreon-interpolated')
@endforeach
</div>
@endif
@endif
@include('modals.partials.download-subtitles')
@endauth

View File

@@ -0,0 +1,28 @@
<div class="flex flex-col">
<a data-te-collapse-init data-te-ripple-init data-te-ripple-color="light" href="#collapseBackupDownloads" role="button" aria-expanded="false" aria-controls="collapseBackupDownloads" class="text-sm text-center cursor-pointer pt-2 text-rose-600">Backup server</a>
<div class="!visible hidden" id="collapseBackupDownloads" data-te-collapse-item>
<div class="block rounded-lg bg-neutral-200 p-6 dark:bg-neutral-900 dark:text-neutral-50">
<p class="text-sm text-red-600">Only use these if you live in Asia (e.g. Singapore/Philippines/...) and experience slow downloads (~10-100kbps) with the normal download links!</p>
<div class="flex flex-wrap justify-around">
@foreach ($dlList as $hdl)
@php
$download = $hdl->getDownloadByType('FHD');
$downloadURL = $dlDomainsBackup[array_rand($dlDomainsBackup)].'/'.$download->url;
$background = 'bg-green-600';
@endphp
<livewire:download-button
:download-url="$downloadURL"
:download-id="$download->id"
:download-count="$download->count"
:episode-number="$hdl->episode"
:fill-numbers="$fillNumbers"
:file-size="$download->getFileSize()"
:background="$background">
@endforeach
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,18 @@
@if(is_null($hdl->getDownloadByType('FHDi')))
<a class="inline-flex items-center ml-4 px-4 p-4 mt-4 cursor-not-allowed bg-gray-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150" id="downloadEpisode" alt="Kekw">
<i class="fa-solid fa-download mr-2"></i> Episode {{ $hdl->episode }} (Unavailable)
</a>
@else
@php
$download = $hdl->getDownloadByType('FHDi');
$downloadURL = $dldomains[array_rand($dldomains)].'/'.$download->url;
@endphp
<livewire:download-button
:download-url="$downloadURL"
:download-id="$download->id"
:download-count="$download->count"
:episode-number="$hdl->episode"
:fill-numbers="$fillNumbers"
:file-size="$download->getFileSize()">
@endif

View File

@@ -0,0 +1,24 @@
@if(is_null($hdl->getDownloadByType('UHDi')))
<a class="inline-flex items-center ml-4 px-4 p-4 mt-4 cursor-not-allowed bg-gray-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150" id="downloadEpisode" alt="Kekw">
<i class="fa-solid fa-download mr-2"></i> Episode {{ $hdl->episode }} (Unavailable)
</a>
@else
@php
$download = $hdl->getDownloadByType('UHDi');
$now = \Illuminate\Support\Carbon::now();
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
@endphp
<livewire:download-button
:download-url="$downloadURL"
:download-id="$download->id"
:download-count="$download->count"
:episode-number="$hdl->episode"
:fill-numbers="$fillNumbers"
:file-size="$download->getFileSize()">
@endif

View File

@@ -0,0 +1,24 @@
@if(is_null($hdl->getDownloadByType('UHD')))
<a class="inline-flex items-center ml-4 px-4 p-4 mt-4 cursor-not-allowed bg-gray-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150" id="downloadEpisode" alt="Kekw">
<i class="fa-solid fa-download mr-2"></i> Episode {{ $hdl->episode }} (Unavailable)
</a>
@else
@php
$download = $hdl->getDownloadByType('UHD');
$now = \Illuminate\Support\Carbon::now();
$expire = \Illuminate\Support\Facades\Crypt::encryptString($now->addHours(6));
$file = \Illuminate\Support\Facades\Crypt::encryptString('hentai/'.$download->url);
$downloadURL = $dlpdomains[array_rand($dlpdomains)].'/download/'.$file.'/'.$expire;
@endphp
<livewire:download-button
:download-url="$downloadURL"
:download-id="$download->id"
:download-count="$download->count"
:episode-number="$hdl->episode"
:fill-numbers="$fillNumbers"
:file-size="$download->getFileSize()">
@endif

Some files were not shown because too many files have changed in this diff Show More