mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Update remove crap managment
This commit is contained in:
+16
-7
@@ -251,7 +251,7 @@ class Books
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function processBookReleases(): void
|
||||
public function processBookReleases(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
$bookids = [];
|
||||
if (ctype_digit((string) $this->bookreqids)) {
|
||||
@@ -263,13 +263,22 @@ class Books
|
||||
$total = \count($bookids);
|
||||
if ($total > 0) {
|
||||
foreach ($bookids as $i => $iValue) {
|
||||
$query = Release::query()
|
||||
->whereNull('bookinfo_id')
|
||||
->whereIn('categories_id', [$iValue])
|
||||
->orderByDesc('postdate')
|
||||
->limit($this->bookqty);
|
||||
|
||||
if ($guidChar !== '') {
|
||||
$query->where('leftguid', 'like', $guidChar.'%');
|
||||
}
|
||||
|
||||
if ($groupID !== '') {
|
||||
$query->where('groups_id', $groupID);
|
||||
}
|
||||
|
||||
$this->processBookReleasesHelper(
|
||||
Release::query()
|
||||
->whereNull('bookinfo_id')
|
||||
->whereIn('categories_id', [$iValue])
|
||||
->orderByDesc('postdate')
|
||||
->limit($this->bookqty)
|
||||
->get(['searchname', 'id', 'categories_id']), $iValue
|
||||
$query->get(['searchname', 'id', 'categories_id']), $iValue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,12 @@ class TmuxRun extends Tmux
|
||||
$log = $this->writelog($runVar['panes']['two'][2]);
|
||||
shell_exec(
|
||||
"tmux respawnp -t{$runVar['constants']['tmux_session']}:2.2 ' \
|
||||
{$runVar['commands']['_phpn']} {$runVar['paths']['misc']}update/postprocess.php amazon true $log; date +\"{$this->_dateFormat}\"; {$runVar['commands']['_sleep']} {$runVar['settings']['post_timer_amazon']}' 2>&1 1> /dev/null"
|
||||
{$runVar['commands']['_phpn']} artisan update:postprocess music true 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_phpn']} artisan update:postprocess book true 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_phpn']} artisan update:postprocess console true 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_phpn']} artisan update:postprocess games true 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_phpn']} artisan update:postprocess xxx true 2>&1 | tee -a $log; \
|
||||
date +\"{$this->_dateFormat}\"; {$runVar['commands']['_sleep']} {$runVar['settings']['post_timer_amazon']}' 2>&1 1> /dev/null"
|
||||
);
|
||||
break;
|
||||
case (int) $runVar['settings']['post_amazon'] === 1 && (int) $runVar['settings']['processbooks'] === 0
|
||||
@@ -239,7 +244,7 @@ class TmuxRun extends Tmux
|
||||
"tmux respawnp -t{$runVar['constants']['tmux_session']}:2.1 ' \
|
||||
{$runVar['commands']['_php']} artisan multiprocessing:postprocess tv 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_php']} artisan multiprocessing:postprocess mov 2>&1 | tee -a $log; \
|
||||
{$runVar['commands']['_php']} {$runVar['paths']['misc']}update/postprocess.php anime true $log; \
|
||||
{$runVar['commands']['_php']} artisan multiprocessing:postprocess anime 2>&1 | tee -a $log; \ $log; \
|
||||
{$runVar['commands']['_php']} {$runVar['paths']['misc']}testing/PostProc/check_covers.php true $log; \
|
||||
date +\"{$this->_dateFormat}\"; {$runVar['commands']['_sleep']} {$runVar['settings']['post_timer_non']}' 2>&1 1> /dev/null"
|
||||
);
|
||||
|
||||
@@ -199,13 +199,17 @@ class Forking
|
||||
break;
|
||||
|
||||
case 'postProcess_ama':
|
||||
$this->processSingle();
|
||||
$this->postProcessRunner->processBooks();
|
||||
break;
|
||||
|
||||
case 'postProcess_add':
|
||||
$this->postProcessRunner->processAdditional();
|
||||
break;
|
||||
|
||||
case 'postProcess_ani':
|
||||
$this->postProcessRunner->processAnime();
|
||||
break;
|
||||
|
||||
case 'postProcess_mov':
|
||||
$renamedOnly = (isset($this->workTypeOptions[0]) && $this->workTypeOptions[0] === true);
|
||||
$this->postProcessRunner->processMovies($renamedOnly);
|
||||
|
||||
@@ -212,4 +212,68 @@ class PostProcessRunner extends BaseRunner
|
||||
|
||||
return count(DB::select($checkSql)) > 0;
|
||||
}
|
||||
|
||||
public function processAnime(): void
|
||||
{
|
||||
if ((int) Settings::settingValue('lookupanidb') <= 0) {
|
||||
$this->headerNone();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$checkSql = '
|
||||
SELECT id
|
||||
FROM releases
|
||||
WHERE categories_id = 5070
|
||||
AND anidbid IS NULL
|
||||
LIMIT 1';
|
||||
if (count(DB::select($checkSql)) === 0) {
|
||||
$this->headerNone();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT DISTINCT LEFT(leftguid, 1) AS id
|
||||
FROM releases
|
||||
WHERE categories_id = 5070
|
||||
AND anidbid IS NULL
|
||||
LIMIT 16';
|
||||
$queue = DB::select($sql);
|
||||
|
||||
$maxProcesses = (int) Settings::settingValue('postthreadsnon');
|
||||
$this->runPostProcess($queue, $maxProcesses, 'anime', 'anime postprocessing');
|
||||
}
|
||||
|
||||
public function processBooks(): void
|
||||
{
|
||||
if ((int) Settings::settingValue('lookupbooks') <= 0) {
|
||||
$this->headerNone();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$checkSql = '
|
||||
SELECT id
|
||||
FROM releases
|
||||
WHERE categories_id BETWEEN 7000 AND 7999
|
||||
AND bookinfo_id IS NULL
|
||||
LIMIT 1';
|
||||
if (count(DB::select($checkSql)) === 0) {
|
||||
$this->headerNone();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT DISTINCT LEFT(leftguid, 1) AS id
|
||||
FROM releases
|
||||
WHERE categories_id BETWEEN 7000 AND 7999
|
||||
AND bookinfo_id IS NULL
|
||||
LIMIT 16';
|
||||
$queue = DB::select($sql);
|
||||
|
||||
$maxProcesses = (int) Settings::settingValue('postthreadsnon');
|
||||
$this->runPostProcess($queue, $maxProcesses, 'books', 'books postprocessing');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,23 +112,27 @@ class PostProcess
|
||||
/**
|
||||
* Lookup anidb if enabled.
|
||||
*
|
||||
* @param string $groupID (Optional) ID of a group to work on.
|
||||
* @param string $guidChar (Optional) First letter of a release GUID to use to get work.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function processAnime(): void
|
||||
public function processAnime(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
$this->animeProcessor->process();
|
||||
$this->animeProcessor->process($groupID, $guidChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process books using amazon.com.
|
||||
*
|
||||
* @param string $groupID (Optional) ID of a group to work on.
|
||||
* @param string $guidChar (Optional) First letter of a release GUID to use to get work.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function processBooks(): void
|
||||
public function processBooks(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
$this->booksProcessor->process();
|
||||
$this->booksProcessor->process($groupID, $guidChar);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,14 +56,26 @@ class AniDB
|
||||
/**
|
||||
* Queues anime releases for processing.
|
||||
*
|
||||
* @param string $groupID (Optional) ID of a group to work on.
|
||||
* @param string $guidChar (Optional) First letter of a release GUID to use to get work.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function processAnimeReleases(): void
|
||||
public function processAnimeReleases(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
$results = Release::query()
|
||||
$query = Release::query()
|
||||
->whereNull('anidbid')
|
||||
->where('categories_id', Category::TV_ANIME)
|
||||
->orderByDesc('postdate')
|
||||
->where('categories_id', Category::TV_ANIME);
|
||||
|
||||
if ($guidChar !== '') {
|
||||
$query->where('leftguid', 'like', $guidChar.'%');
|
||||
}
|
||||
|
||||
if ($groupID !== '') {
|
||||
$query->where('groups_id', $groupID);
|
||||
}
|
||||
|
||||
$results = $query->orderByDesc('postdate')
|
||||
->limit($this->aniqty)
|
||||
->get();
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class PostProcessGuid extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'postprocess:guid
|
||||
{type : Type: additional, nfo, movie, or tv}
|
||||
{type : Type: additional, nfo, movie, tv, anime, or books}
|
||||
{guid : First character of release guid (a-f, 0-9)}
|
||||
{renamed? : For movie/tv: process renamed only (optional)}';
|
||||
|
||||
@@ -70,8 +70,16 @@ class PostProcessGuid extends Command
|
||||
(new PostProcess)->processTv('', $guid, $renamed);
|
||||
break;
|
||||
|
||||
case 'anime':
|
||||
(new PostProcess)->processAnime('', $guid);
|
||||
break;
|
||||
|
||||
case 'books':
|
||||
(new PostProcess)->processBooks('', $guid);
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->error('Invalid type. Must be: additional, nfo, movie, or tv.');
|
||||
$this->error('Invalid type. Must be: additional, nfo, movie, tv, anime, or books.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class ProcessPostProcess extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'multiprocessing:postprocess
|
||||
{type : Type: ama, add, mov, nfo, sha, or tv}
|
||||
{type : Type: ama, add, ani, mov, nfo, sha, or tv}
|
||||
{renamed=false : For mov/tv: only post-process renamed releases (true/false)}';
|
||||
|
||||
/**
|
||||
@@ -31,11 +31,12 @@ class ProcessPostProcess extends Command
|
||||
$type = $this->argument('type');
|
||||
$renamed = $this->argument('renamed');
|
||||
|
||||
if (! \in_array($type, ['ama', 'add', 'mov', 'nfo', 'sha', 'tv'], true)) {
|
||||
$this->error('Type must be one of: ama, add, mov, nfo, sha, tv');
|
||||
if (! \in_array($type, ['ama', 'add', 'ani', 'mov', 'nfo', 'sha', 'tv'], true)) {
|
||||
$this->error('Type must be one of: ama, add, ani, mov, nfo, sha, tv');
|
||||
$this->line('');
|
||||
$this->line('ama => Do amazon processing (no multiprocessing due to API restrictions)');
|
||||
$this->line('ama => Do amazon/books processing');
|
||||
$this->line('add => Do additional (rar|zip) processing');
|
||||
$this->line('ani => Do anime processing');
|
||||
$this->line('mov => Do movie processing');
|
||||
$this->line('nfo => Do NFO processing');
|
||||
$this->line('sha => Do sharing processing (no multiprocessing)');
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Blacklight\ReleaseRemover;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RemoveCrapReleases extends Command
|
||||
{
|
||||
protected $signature = 'releases:remove-crap
|
||||
{--type= : Type of crap to remove}
|
||||
{--time=full : Time limit in hours or full}
|
||||
{--blacklist-id= : Specific blacklist ID}
|
||||
{--delete : Actually delete releases}';
|
||||
|
||||
protected $description = 'Remove crap releases based on various criteria';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$type = $this->option('type') ?? '';
|
||||
$time = $this->option('time') ?? 'full';
|
||||
$blacklistId = $this->option('blacklist-id') ?? '';
|
||||
$delete = $this->option('delete');
|
||||
|
||||
if (! $delete) {
|
||||
$this->warn('Running in DRY-RUN mode. Use --delete to actually remove releases.');
|
||||
}
|
||||
|
||||
try {
|
||||
$remover = new ReleaseRemover;
|
||||
$result = $remover->removeCrap($delete, $time, $type, $blacklistId);
|
||||
|
||||
if ($result === true) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->error($result);
|
||||
|
||||
return self::FAILURE;
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,17 @@ class AdminTmuxController extends BasePageController
|
||||
|
||||
switch ($action) {
|
||||
case 'submit':
|
||||
Settings::settingsUpdate($request->all());
|
||||
$data = $request->all();
|
||||
|
||||
// Handle fix_crap checkbox array - convert to comma-separated string
|
||||
if (isset($data['fix_crap']) && is_array($data['fix_crap'])) {
|
||||
$data['fix_crap'] = implode(',', $data['fix_crap']);
|
||||
} elseif (! isset($data['fix_crap'])) {
|
||||
// If no checkboxes selected, save empty string
|
||||
$data['fix_crap'] = '';
|
||||
}
|
||||
|
||||
Settings::settingsUpdate($data);
|
||||
|
||||
return redirect()->to('admin/tmux-edit')->with('success', 'Tmux settings updated successfully');
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ class AnimeProcessor
|
||||
$this->echooutput = $echooutput;
|
||||
}
|
||||
|
||||
public function process(): void
|
||||
public function process(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
if ((int) Settings::settingValue('lookupanidb') !== 0) {
|
||||
(new AniDB)->processAnimeReleases();
|
||||
(new AniDB)->processAnimeReleases($groupID, $guidChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ class BooksProcessor
|
||||
$this->echooutput = $echooutput;
|
||||
}
|
||||
|
||||
public function process(): void
|
||||
public function process(string $groupID = '', string $guidChar = ''): void
|
||||
{
|
||||
if ((int) Settings::settingValue('lookupbooks') !== 0) {
|
||||
(new Books)->processBookReleases();
|
||||
(new Books)->processBookReleases($groupID, $guidChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,12 @@ class TmuxPaneManager
|
||||
{
|
||||
$killFlag = $kill ? '-k' : '';
|
||||
|
||||
// Escape the command for tmux by replacing double quotes with escaped quotes
|
||||
$escapedCommand = str_replace('"', '\\"', $command);
|
||||
$escapedCommand = str_replace('$', '\\$', $escapedCommand);
|
||||
|
||||
$result = Process::timeout(10)->run(
|
||||
"tmux respawnp {$killFlag} -t {$this->sessionName}:{$target} '{$command}'"
|
||||
"tmux respawnp {$killFlag} -t {$this->sessionName}:{$target} \"{$escapedCommand}\""
|
||||
);
|
||||
|
||||
return $result->successful();
|
||||
|
||||
@@ -71,7 +71,7 @@ class TmuxTaskRunner
|
||||
// Add sleep timer at the end if specified
|
||||
if (isset($options['sleep'])) {
|
||||
$sleepCommand = $this->buildSleepCommand($options['sleep']);
|
||||
$parts[] = "date +'%Y-%m-%d %T'";
|
||||
$parts[] = 'date +"%Y-%m-%d %T"';
|
||||
$parts[] = $sleepCommand;
|
||||
}
|
||||
|
||||
@@ -334,16 +334,12 @@ class TmuxTaskRunner
|
||||
*/
|
||||
protected function runMainSequential(array $runVar): bool
|
||||
{
|
||||
// Full sequential mode - runs sequential script
|
||||
// Full sequential mode - runs group:update-all for each group
|
||||
$pane = '0.1';
|
||||
$script = base_path('misc/update/nix/tmux/bin/sequential.php');
|
||||
|
||||
if (! file_exists($script)) {
|
||||
return $this->disablePane($pane, 'Sequential', 'script not found');
|
||||
}
|
||||
|
||||
$niceness = Settings::settingValue('niceness') ?? 2;
|
||||
$command = "nice -n{$niceness} php {$script}";
|
||||
$artisan = base_path('artisan');
|
||||
$command = "nice -n{$niceness} php {$artisan} group:update-all";
|
||||
$command = $this->buildCommand($command, ['log_pane' => 'sequential']);
|
||||
|
||||
return $this->paneManager->respawnPane($pane, $command);
|
||||
@@ -387,26 +383,115 @@ class TmuxTaskRunner
|
||||
*/
|
||||
protected function runRemoveCrapTask(array $runVar): bool
|
||||
{
|
||||
$enabled = (int) ($runVar['settings']['fix_crap'] ?? 0);
|
||||
$option = $runVar['settings']['fix_crap_opt'] ?? 'Disabled';
|
||||
$pane = '1.1';
|
||||
|
||||
if ($enabled !== 1) {
|
||||
// Handle disabled state
|
||||
if ($option === 'Disabled' || $option === 0 || $option === '0') {
|
||||
return $this->disablePane($pane, 'Remove Crap', 'disabled in settings');
|
||||
}
|
||||
|
||||
$option = (int) ($runVar['settings']['fix_crap_opt'] ?? 1);
|
||||
$script = base_path('misc/update/nix/tmux/bin/removecrap.php');
|
||||
$niceness = Settings::settingValue('niceness') ?? 2;
|
||||
$artisan = base_path('artisan');
|
||||
$sleep = (int) ($runVar['settings']['crap_timer'] ?? 300);
|
||||
|
||||
if (! file_exists($script)) {
|
||||
return $this->disablePane($pane, 'Remove Crap', 'script not found');
|
||||
// Handle 'All' mode - run all types with 2 hour time limit
|
||||
if ($option === 'All') {
|
||||
$command = "nice -n{$niceness} php {$artisan} releases:remove-crap --time=2 --delete";
|
||||
$command = $this->buildCommand($command, ['log_pane' => 'removecrap', 'sleep' => $sleep]);
|
||||
// Wrap in while loop to keep pane alive
|
||||
$loopCommand = "while true; do {$command}; done";
|
||||
|
||||
return $this->paneManager->respawnPane($pane, $loopCommand);
|
||||
}
|
||||
|
||||
$niceness = Settings::settingValue('niceness') ?? 2;
|
||||
$command = "nice -n{$niceness} php {$script} {$option}";
|
||||
$sleep = (int) ($runVar['settings']['crap_timer'] ?? 300);
|
||||
$command = $this->buildCommand($command, ['log_pane' => 'removecrap', 'sleep' => $sleep]);
|
||||
// Handle 'Custom' mode - cycle through selected types
|
||||
if ($option === 'Custom') {
|
||||
$selectedTypes = $runVar['settings']['fix_crap'] ?? '';
|
||||
|
||||
return $this->paneManager->respawnPane($pane, $command);
|
||||
if (empty($selectedTypes)) {
|
||||
return $this->disablePane($pane, 'Remove Crap', 'no crap types selected');
|
||||
}
|
||||
|
||||
$types = is_array($selectedTypes) ? $selectedTypes : explode(',', $selectedTypes);
|
||||
$types = array_filter($types); // Remove empty values
|
||||
|
||||
if (empty($types)) {
|
||||
return $this->disablePane($pane, 'Remove Crap', 'no crap types selected');
|
||||
}
|
||||
|
||||
// Get the next type to process
|
||||
$stateFile = storage_path('tmux/removecrap_state.json');
|
||||
$state = $this->loadCrapState($stateFile);
|
||||
|
||||
// Determine current type index
|
||||
$currentIndex = $state['current_index'] ?? 0;
|
||||
$isFirstRun = $state['first_run'] ?? true;
|
||||
|
||||
// Validate index
|
||||
if ($currentIndex >= count($types)) {
|
||||
$currentIndex = 0;
|
||||
$isFirstRun = false;
|
||||
}
|
||||
|
||||
$currentType = $types[$currentIndex];
|
||||
|
||||
// Determine time limit: full on first run, 4 hours otherwise
|
||||
$time = $isFirstRun ? 'full' : '4';
|
||||
|
||||
// Build and run command
|
||||
$command = "nice -n{$niceness} php {$artisan} releases:remove-crap --type={$currentType} --time={$time} --delete";
|
||||
$command = $this->buildCommand($command, ['log_pane' => 'removecrap', 'sleep' => $sleep]);
|
||||
|
||||
// Update state for next run
|
||||
$nextIndex = $currentIndex + 1;
|
||||
if ($nextIndex >= count($types)) {
|
||||
$nextIndex = 0;
|
||||
$isFirstRun = false;
|
||||
}
|
||||
|
||||
$this->saveCrapState($stateFile, [
|
||||
'current_index' => $nextIndex,
|
||||
'first_run' => $isFirstRun,
|
||||
'types' => $types,
|
||||
]);
|
||||
|
||||
// Wrap in while loop to keep pane alive
|
||||
$loopCommand = "while true; do {$command}; done";
|
||||
|
||||
return $this->paneManager->respawnPane($pane, $loopCommand);
|
||||
}
|
||||
|
||||
// Default fallback - disabled
|
||||
return $this->disablePane($pane, 'Remove Crap', 'invalid configuration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load crap removal state
|
||||
*/
|
||||
protected function loadCrapState(string $file): array
|
||||
{
|
||||
if (! file_exists($file)) {
|
||||
return ['current_index' => 0, 'first_run' => true];
|
||||
}
|
||||
|
||||
$content = file_get_contents($file);
|
||||
$state = json_decode($content, true);
|
||||
|
||||
return $state ?: ['current_index' => 0, 'first_run' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save crap removal state
|
||||
*/
|
||||
protected function saveCrapState(string $file, array $state): void
|
||||
{
|
||||
$dir = dirname($file);
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($file, json_encode($state, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -627,3 +627,22 @@ code.api-token::-webkit-scrollbar-thumb:hover {
|
||||
}
|
||||
}
|
||||
|
||||
/* Tmux Edit - Remove Crap Types Container Styles */
|
||||
#crap_types_container {
|
||||
transition: opacity 0.3s ease, display 0.3s ease;
|
||||
}
|
||||
|
||||
/* Checkbox hover effects for crap types */
|
||||
.hover\:bg-gray-100:hover {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.dark .hover\:bg-gray-700:hover {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
/* Form checkbox focus ring */
|
||||
.form-checkbox:focus-visible {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
+63
-13
@@ -383,7 +383,7 @@ window.showConfirm = function(options) {
|
||||
const messageText = document.getElementById('confirmationModalMessage');
|
||||
const confirmText = document.getElementById('confirmationModalConfirmText');
|
||||
const cancelText = document.getElementById('confirmationModalCancelText');
|
||||
|
||||
|
||||
if (titleText) titleText.textContent = config.title;
|
||||
if (messageText) messageText.textContent = config.message;
|
||||
if (confirmText) confirmText.textContent = config.confirmText;
|
||||
@@ -2649,7 +2649,7 @@ function initMoviesLayoutToggle() {
|
||||
applyLayout(currentLayout); // Apply layout anyway
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fetch('/movies/update-layout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -2771,29 +2771,29 @@ function initAdminDashboardCharts() {
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
initializeAllDashboardCharts();
|
||||
}
|
||||
|
||||
function initializeAllDashboardCharts() {
|
||||
// Check if any chart canvas elements exist
|
||||
const hasUserStatsCharts = document.getElementById('downloadsChart') ||
|
||||
const hasUserStatsCharts = document.getElementById('downloadsChart') ||
|
||||
document.getElementById('apiHitsChart') ||
|
||||
document.getElementById('downloadsMinuteChart') ||
|
||||
document.getElementById('apiHitsMinuteChart');
|
||||
|
||||
|
||||
const hasSystemCharts = document.getElementById('cpuHistory24hChart') ||
|
||||
document.getElementById('ramHistory24hChart') ||
|
||||
document.getElementById('cpuHistory30dChart') ||
|
||||
document.getElementById('ramHistory30dChart');
|
||||
|
||||
|
||||
if (!hasUserStatsCharts && !hasSystemCharts) {
|
||||
return; // Not on admin dashboard or charts not present
|
||||
}
|
||||
|
||||
// Initialize user statistics charts
|
||||
initializeUserStatCharts();
|
||||
|
||||
|
||||
// Initialize system metrics charts
|
||||
initializeSystemMetricsCharts();
|
||||
}
|
||||
@@ -3477,8 +3477,8 @@ function initAdminRegexFormValidation() {
|
||||
// and have matching delimiters
|
||||
if (value.length > 0) {
|
||||
const firstChar = value[0];
|
||||
const delimiters = ['/', '#', '~', '%', '@', '!'];
|
||||
|
||||
const delimiters = ['/', '#', '~', '%', '@', '!',];
|
||||
|
||||
if (delimiters.includes(firstChar)) {
|
||||
// Check if regex has matching closing delimiter
|
||||
let delimiterCount = 0;
|
||||
@@ -3531,8 +3531,8 @@ function initAdminRegexFormValidation() {
|
||||
const value = this.value.trim();
|
||||
if (value) {
|
||||
const firstChar = value[0];
|
||||
const delimiters = ['/', '#', '~', '%', '@', '!'];
|
||||
|
||||
const delimiters = ['/', '#', '~', '%', '@', '!',];
|
||||
|
||||
if (delimiters.includes(firstChar)) {
|
||||
let delimiterCount = 0;
|
||||
for (let i = 1; i < value.length; i++) {
|
||||
@@ -3541,7 +3541,7 @@ function initAdminRegexFormValidation() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (delimiterCount === 0) {
|
||||
this.classList.add('border-red-500');
|
||||
this.classList.remove('border-green-500');
|
||||
@@ -3564,7 +3564,7 @@ function initAdminTmuxSelectColors() {
|
||||
// This function is for future tmux color selection functionality
|
||||
// Currently, no color selection is needed in tmux settings
|
||||
// If color pickers are added to tmux-edit page in the future, add them here
|
||||
|
||||
|
||||
// Check if we're on a page that might have color inputs
|
||||
const colorInputs = document.querySelectorAll('input[type="color"], select[data-color-select]');
|
||||
if (colorInputs.length > 0) {
|
||||
@@ -4248,3 +4248,53 @@ function initFlashMessages() {
|
||||
initFlashMessages();
|
||||
}
|
||||
})();
|
||||
|
||||
// Tmux Edit - Remove Crap Releases Toggle
|
||||
function toggleCrapTypes(value) {
|
||||
const container = document.getElementById('crap_types_container');
|
||||
if (container) {
|
||||
if (value === 'Custom') {
|
||||
container.style.display = 'block';
|
||||
// Add a slight animation effect
|
||||
setTimeout(() => {
|
||||
container.style.opacity = '1';
|
||||
}, 10);
|
||||
} else {
|
||||
container.style.opacity = '0.5';
|
||||
setTimeout(() => {
|
||||
container.style.display = 'none';
|
||||
container.style.opacity = '1';
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize tmux edit page functionality
|
||||
function initTmuxEdit() {
|
||||
// Initialize crap types toggle on page load
|
||||
const checkedRadio = document.querySelector('input[name="fix_crap_opt"]:checked');
|
||||
if (checkedRadio) {
|
||||
toggleCrapTypes(checkedRadio.value);
|
||||
}
|
||||
|
||||
// Add event listeners to all radio buttons
|
||||
document.querySelectorAll('input[name="fix_crap_opt"]').forEach(function(radio) {
|
||||
radio.addEventListener('change', function() {
|
||||
toggleCrapTypes(this.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on DOMContentLoaded if on tmux-edit page
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (document.getElementById('crap_types_container')) {
|
||||
initTmuxEdit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (document.getElementById('crap_types_container')) {
|
||||
initTmuxEdit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,15 +304,41 @@
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Remove Crap Releases</h2>
|
||||
<div class="space-y-4">
|
||||
<x-form.group label="Remove Crap Releases" for="fix_crap_opt" help="Remove passworded and other junk releases.">
|
||||
<x-select id="fix_crap_opt" name="fix_crap_opt" class="w-full">
|
||||
@foreach($yesno_ids as $index => $val)
|
||||
<option value="{{ $val }}" {{ ($site['fix_crap_opt'] ?? '') == $val ? 'selected' : '' }}>
|
||||
{{ $yesno_names[$index] }}
|
||||
</option>
|
||||
<div class="space-y-2">
|
||||
@foreach($fix_crap_radio_ids as $index => $val)
|
||||
<label class="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="radio"
|
||||
id="fix_crap_opt_{{ $val }}"
|
||||
name="fix_crap_opt"
|
||||
value="{{ $val }}"
|
||||
{{ ($site['fix_crap_opt'] ?? 'Disabled') == $val ? 'checked' : '' }}
|
||||
class="form-radio h-4 w-4 text-blue-600 dark:bg-gray-700 dark:border-gray-600">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">{{ $fix_crap_radio_names[$index] }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</x-select>
|
||||
</div>
|
||||
</x-form.group>
|
||||
|
||||
<div id="crap_types_container">
|
||||
<x-form.group label="Select Crap Types" for="fix_crap_types" help="Choose which types of crap releases to remove.">
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border-2 border-blue-200 dark:border-blue-700">
|
||||
@php
|
||||
$selectedTypes = explode(',', $site['fix_crap'] ?? '');
|
||||
@endphp
|
||||
@foreach($fix_crap_check_ids as $index => $val)
|
||||
<label class="flex items-center space-x-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded transition-colors">
|
||||
<input type="checkbox"
|
||||
name="fix_crap[]"
|
||||
value="{{ $val }}"
|
||||
{{ in_array($val, $selectedTypes) ? 'checked' : '' }}
|
||||
class="form-checkbox h-4 w-4 text-blue-600 rounded dark:bg-gray-700 dark:border-gray-600">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ ucfirst($fix_crap_check_names[$index]) }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-form.group>
|
||||
</div>
|
||||
|
||||
<x-form.group label="Remove Crap Sleep Timer" for="crap_timer">
|
||||
<div class="flex gap-2">
|
||||
<x-input id="crap_timer" name="crap_timer" type="number" value="{{ $site['crap_timer'] ?? 300 }}" class="flex-1" />
|
||||
@@ -322,6 +348,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Console & Monitoring Tools -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Console & Monitoring Tools</h2>
|
||||
|
||||
Reference in New Issue
Block a user