Files
MeDBia/videodb/index.php
Malin f55c91276e feat: add videodb media index with Docker stack
- Add videodb PHP/MySQL media collection manager (Blu-ray, DVD, CD)
- Dockerfile: PHP 8.1 + Apache with GD/mysqli/exif extensions
- docker-compose.yml: app on port 6761 + MySQL 8.0 with health checks
- docker-entrypoint.sh: auto-generates config.inc.php from env vars,
  waits for MySQL, initializes DB schema idempotently
- init-db.php: CLI schema installer using app's own prefix_query() logic
- Persistent volumes for DB, cache, and cover images

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 09:49:52 +02:00

346 lines
11 KiB
PHP

<?php
/**
* Browse View
*
* Lets you browse through your movie collection
*
* @package videoDB
* @author Andreas Gohr <a.gohr@web.de>
* @author Andreas Götz <cpuidle@gmx.de>
* @author Chinamann <chinamann@users.sourceforge.net>
* @link http://videodb.sf.net
* @version $Id: index.php,v 2.102 2013/03/21 16:27:57 andig2 Exp $
*/
require_once './core/functions.php';
require_once './core/output.php';
/**
* input
*/
$id = req_int('id');
$diskid = req_string('diskid');
// start session'd settings
$filter = req_string('filter');
$showtv = req_int('showtv');
$listcolumns = req_int('listcolumns');
$mediafilter = req_int('mediafilter');
$order = req_int('order');
// end session'd settings
$owner = req_string('owner');
$ajax_quicksearch = req_string('ajax_quicksearch'); // elegant template only
$quicksearch = req_string('quicksearch'); // elegant template only
$export = req_string('export');
$pageno = (req_string('pageno') == 'all' ? 'all' : req_int('pageno'));
$ajax_render = req_int('ajax_render'); // elegant template only
$deleteid = req_int('deleteid');
/**
* Update item list asynchronously
*
* @author Andreas Goetz <cpuidle@gmx.de>
*/
function ajax_render()
{
global $smarty, $result, $filter, $config;
global $pageno, $totalpages, $totalresults;
// TODO Smarty caching would require further efforts $smarty->caching = 1;
# if (!$smarty->is_cached('list.tpl', get_current_user_id().'|'.$filter.$pageno)) {
// add some delay for debugging
if ($config['debug'] && $_SERVER['SERVER_ADDR'] == '127.0.0.1') usleep(rand(200,1000)*1000);
// load languages and config into Smarty
tpl_language();
tpl_list($result);
// TODO consider pagination
$content = $smarty->fetch('list.tpl', get_current_user_id().'|'.$filter.$pageno);
header('X-JSON: '.json_encode(array('totalresults' => $totalresults ? $totalresults : count($result),
'maxpageno' => $totalpages)));
exit($content);
}
function prepareOrder($m) {
switch($m) {
case 1:
$ORDER = "rating desc";
break;
case 2:
$ORDER = "created DESC, lastupdate DESC";
break;
default:
$ORDER = "title, subtitle asc";
break;
}
return $ORDER;
}
function get_mediatype_sql($m)
{
switch ($m) {
case -2:
$mediatype = '1=1';
break;
case -1:
$mediatype = 'mediatype != '.MEDIA_WISHLIST;
break;
default:
$mediatype = 'mediatype = '.$m;
}
return $mediatype;
}
// set defaults and update session
session_default('filter', $config['filterdefault']);
session_default('showtv', $config['showtv']);
session_default('listcolumns', $config['listcolumns']);
session_default('mediafilter', -1);
session_default('order', -1);
// enable redirects to last list view for delete.php
session_set('listview', 'index.php');
// standard filters
$filter_expr = array(
'NUM' => '^["\\\' ]*[^A-Za-zÄäÖöÜüß]',
'ABC' => '^["\\\' ]*[ABCabcÄä]',
'DEF' => '^["\\\' ]*[DEFdef]',
'GHI' => '^["\\\' ]*[GHIghi]',
'JKL' => '^["\\\' ]*[JKLjkl]',
'MNO' => '^["\\\' ]*[MNOmnoÖö]',
'PQRS' => '^["\\\' ]*[PQRSpqrsß]',
'TUV' => '^["\\\' ]*[TUVtuvÜü]',
'WXYZ' => '^["\\\' ]*[WXZwxy]'
);
if ($filter == 'wanted') $mediafilter = MEDIA_WISHLIST;
$WHERES = get_mediatype_sql(($mediafilter) ? $mediafilter : -1);
// create SQL according to selected filter
$JOINS = '';
$LIMIT = '';
// create SQL according to selected filter
switch ($filter)
{
case 'all':
# $WHERES = 'mediatype != '.MEDIA_WISHLIST;
if($config['orderallbydisk'])
{
$ORDER = 'diskid asc, title, subtitle';
}
break;
case 'seen':
$WHERES .= ' AND !ISNULL('.TBL_USERSEEN.'.video_id)';# AND mediatype != '.MEDIA_WISHLIST;
break;
case 'unseen':
$WHERES .= ' AND ISNULL('.TBL_USERSEEN.'.video_id)';# AND mediatype != '.MEDIA_WISHLIST;
break;
case 'new':
# $WHERES = 'mediatype != '.MEDIA_WISHLIST;
$ORDER = 'created DESC, lastupdate DESC ';
$LIMIT = ' LIMIT '.$config['shownew'];
break;
case 'wanted':
# $WHERES = 'mediatype = '.MEDIA_WISHLIST;
break;
case 'full': // secret filter for exposing all data
# $WHERES = '1=1';
break;
default:
// make sure filter is valid
if (!array_key_exists($filter, $filter_expr)) $filter = 'ABC';
// apply filter
$WHERES .= ' AND title RLIKE \''.mb_convert_encoding($filter_expr[$filter], 'UTF-8', 'ISO-8859-1').'\'';# AND mediatype != '.MEDIA_WISHLIST;
}
if(!isset($ORDER))
{
if(isset($order))
{
$ORDER = prepareOrder($order);
session_set('order', $order);
} else {
$ORDER = prepareOrder(-1);
session_set('order', -1);
}
}
if (!$showtv) $WHERES .= ' AND istv = 0';
// owner selection for multiuser mode- by default this is the logged in user
// any user has automatically read permissions for his personal data
if ($config['multiuser'])
{
// get owner from session- or use current user
session_default_owner();
// if we don't have read all permissions, limit visibility using cross-user permissions
if (!check_permission(PERM_READ))
{
$JOINS = ' LEFT JOIN '.TBL_PERMISSIONS.' ON '.TBL_DATA.'.owner_id = '.TBL_PERMISSIONS.'.to_uid';
$WHERES .= ' AND '.TBL_PERMISSIONS.'.from_uid = '.get_current_user_id().' AND '.TBL_PERMISSIONS.'.permissions & '.PERM_READ.' != 0';
}
// further limit to single owner
if (html_entity_decode($owner) != $lang['filter_any']) {
$WHERES .= " AND ".TBL_USERS.".name = '".escapeSQL($owner)."'";
}
}
// searching?
if ($ajax_quicksearch || $quicksearch)
{
$qs = escapeSQL($ajax_quicksearch ? $ajax_quicksearch : $quicksearch);
$WHERES .= ' AND (title LIKE "%'.$qs.'%" OR subtitle LIKE "%'.$qs.'%")';
}
// async request for quick-searching within current spec
if ($ajax_quicksearch)
{
// do hard work
$SQL = 'SELECT '.TBL_DATA.'.id, title, subtitle
FROM '.TBL_DATA.'
LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id
LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id()."
$JOINS
WHERE $WHERES
ORDER BY $ORDER
LIMIT 20";
$result = runSQL($SQL);
$ret = '';
foreach ($result as $item)
{
$title = preg_replace('/('.$ajax_quicksearch.')/i', '<em>\1</em>', $item['title']);
if ($item['subtitle']) $title .= ' - '.$item['subtitle'];
$ret .= "<li id='".$item['id']."'>".$title."</li>";
}
$ret = "<ul>$ret</ul>";
exit($ret);
}
// XML / RSS / PDF export
if ($export && array_key_exists($export, $config) && $config[$export])
{
// either (xml|rss|pdf|xls)export
$func = $export.'export';
if ($export == 'rss') $export = 'xml';
require_once './core/'.$export.'.php';
if (function_exists($func)) $func("$JOINS WHERE $WHERES ORDER BY $ORDER $LIMIT");
exit;
}
/*
Calculate pagination
Check to see if user has selected the New items tab.
This is seperately assigned as a LIMIT so, if this exists,
lets just skip page numbers and carry on
*/
if ($LIMIT == '' && ($config['pageno'] > 0) &! ($pageno == 'all'))
{
// start at first page
if (!$pageno) $pageno = ($deleteid) ? session_get('lastpageno', 1) : 1;
session_set('lastpageno', $pageno);
// define Max Results Per Page
$maxresults = $config['pageno'];
// define the Start Number
$from = (($pageno * $maxresults) - $maxresults);
$LIMIT = ' LIMIT '.$from.', '.$maxresults;
// get total amount of results from DB
$totalresults = runSQL('SELECT count(*) AS num
FROM '.TBL_DATA.'
LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id
LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id()."
$JOINS WHERE $WHERES");
$totalresults = (count($totalresults) > 0) ? (int)$totalresults[0]['num'] : 0;
// calculate total amount of pages
$totalpages = ceil($totalresults / $maxresults);
$smarty->assign('pageno', $pageno); // assign current Page Number
$smarty->assign('maxpageno', $totalpages); // set Maximum Pages
$smarty->assign('totalresults', $totalresults); // set Total Records Returned
}
// do hard work
$SQL = 'SELECT '.TBL_DATA.'.id, '.TBL_DATA.'.diskid,
title, subtitle, language, year,
director, plot, imgurl,
owner_id, '.TBL_USERS.'.name AS owner, '.TBL_LENT.'.who,
md5, comment, disklabel, imdbID, actors, runtime,
country, filename, filesize, filedate, audio_codec,
video_codec, video_width, video_height, istv,
lastupdate, mediatype, rating,
custom1, custom2, custom3, custom4,
created, !ISNULL('.TBL_USERSEEN.'.video_id) AS seen,
'.TBL_MEDIATYPES.'.name AS mediatypename
FROM '.TBL_DATA.'
LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id
LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id().'
LEFT JOIN '.TBL_LENT.' ON '.TBL_DATA.'.diskid = '.TBL_LENT.'.diskid
LEFT JOIN '.TBL_MEDIATYPES.' ON '.TBL_DATA.'.mediatype = '.TBL_MEDIATYPES.'.id'."
$JOINS
WHERE $WHERES
ORDER BY $ORDER
$LIMIT";
$result = runSQL($SQL);
// store query result in session for prev/next navigation
session_set('query_result', array_column($result, 'id'));
// process asynchronous refresh
if ($ajax_render)
{
ajax_render();
}
// prepare
tpl_page('browse');
tpl_list($result);
tpl_filters($filter, $showtv);
// caching enabled?
if ($config['http_caching'])
{
require_once('./core/httpcache.php');
httpCacheCaptureStart();
}
$smarty->assign('moreless', true); // show more/less control in list view
// allow data export
foreach (array('xls','pdf','xml','rss') as $export)
{
if (array_key_exists($export, $config) && $config[$export]) $smarty->assign($export, 'index.php?');
}
// display templates
smarty_display('header.tpl');
smarty_display('filters.tpl');
if (!$config['http_caching']) flush();
if ($deleteid) $smarty->assign('deleted', true);
// TODO smarty caching would require further efforts
smarty_display('list.tpl', get_current_user_id().'|'.$WHERES);
smarty_display('footer.tpl');
// caching enabled?
if ($config['http_caching'])
{
httpCacheOutput('index', httpCacheCaptureEnd());
}