rompr www

This commit is contained in:
qo-op 2020-12-12 03:06:33 +01:00
parent d2e02277d2
commit 467a426bf5
1839 changed files with 335422 additions and 0 deletions

41
www/jukebox/404.php Normal file
View File

@ -0,0 +1,41 @@
<?php
include('includes/vars.php');
include("includes/functions.php");
$base_url = get_base_url();
$request = $_SERVER['REQUEST_URI'];
logger::log('REDIRECT','Uri is',$_SERVER['REQUEST_URI']);
if (preg_match('#prefs/userstreams/.*\.jpg#', $request)) {
$redirect = $base_url.'/newimages/broadcast.svg';
logger::log("404", "Request for missing userstream image. Redirecting to ".$redirect);
header("HTTP/1.1 307 Temporary Redirect");
header("Location: ".$redirect);
} else if (preg_match('#prefs/podcasts/.*\.jpg#', $request) ||
preg_match('#prefs/podcasts/.*\.png#', $request)) {
$redirect = $base_url.'/newimages/podcast-logo.svg';
logger::log("404", "Request for missing podcast image. Redirecting to ".$redirect);
header("HTTP/1.1 307 Temporary Redirect");
header("Location: ".$redirect);
} else {
header("HTTP/1.1 404 Not Found");
?>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/layout-january.css" />
<link rel="stylesheet" type="text/css" href="themes/Darkness.css" />
<title>Badgers!</title>
</head>
<body>
<br><br><br>
<table align="center"><tr><td><img src="newimages/favicon-196.png"></td></tr></table>
<h2 align="center">404 Error!</h2>
<br><br>
<h2 align="center">It's all gone horribly wrong</h2>
<br><br>
<?php
print '<h3 align="center">The document &quot;'.$request."&quot; doesn't exist. Are you sure you know what you're doing?</h3>";
?>
</body>
</html>
<?php
}
?>

22
www/jukebox/INSTALL.txt Normal file
View File

@ -0,0 +1,22 @@
==================
= Installing RompR
==================
Please read the installation instructions here:
http://sourceforge.net/p/rompr/wiki/Installation/
MacOS X Users
=============
Please read the instructions here:
http://sourceforge.net/p/rompr/wiki/Installation%20on%20Mac%20OS%20X/
Windows Users
=============
First, install a better operating system, then read this file again :)
Actually, it may well be possible to get this to run on Windows, if mpd or mopidy can be got
to work then there's probably a LAMP stack for Windows that will run apache and php
OK. But I don't intend to go buy a Windows computer just to find out.

23
www/jukebox/LICENSE.txt Normal file
View File

@ -0,0 +1,23 @@
# (C) Fat German Productions/Mark Greenwood 2017
SEE includes/license.html
CREDITS
=======
This program makes use of lots of other stuff which are all released
under their own licenses, just to make it complicated.
The original inspiration came from phpMP, and the original core of this app was lifted
directly from there. It's grown quite a lot since then.
jquery (http://jquery.com) and the jquery UI framework (http://jqueryui.com)
are probably released under the GPL or something. I dunno, their websites aren't
very clear on that point.
The md5 hashing algorithm code was taken from http://pajhome.org.uk/crypt/md5
and is released under a BSD license
The jquery form plugin comes from http://malsup.com/jquery/form/
and also has two licenses.

23
www/jukebox/README.md Normal file
View File

@ -0,0 +1,23 @@
# RompЯ
This is a browser-based client for Mopidy and MPD, which are both music players.
You can use RompЯ to control a music player on another device or on your computer. Because it runs in a web browser you can run it ony any device - your laptop,tablet, or phone can all be used to control your music player.
It has a rich and beautiful interface which is intended to sort your music, manage radio stations, browse and subscribe to podcasts.
When used with Mopidy you can listen to Spotify and make use of RompЯ's incredible music discovery features which will help to introduce you to new music.
## Installation from GitHub
Please see the [New Project Homepage](https://fatg3erman.github.io/RompR/)
![](docs/images/rompr-1.png)
![](docs/images/rompr-on-a-phone.png)
### What people are saying about RompЯ
* “Best interface to mpd / mopidy ever. A real must!”
* “Best browser based frontend to mpd I've ever seen!! Thank you!”
* “Rompr is a wonderful web based interface client to mpd.”
* “Great Project! I use it everyday already. I hope for VK-support soon. Thanks!”
* “I tired now a fiew clients to control my mopidy server running on Raspberry. rompr is far the best one, easy to install, very fast, good design and useful features.”
* “This application is amazing and easy to setup. I love it.”
* “Wow! Great stuff! I was searching for something like that for months!”

13
www/jukebox/README_ru.md Normal file
View File

@ -0,0 +1,13 @@
# RompЯ
Это браузерный клиент для музыкальных проигрывателей Mopidy и MPD.
RompЯ можно использовать для управления музыкальным проигрывателем на удалённом или локальном компьютере. Поскольку он работает в веб-браузере, вы можете запускать его на любом устройстве - ноутбуке, планшете или телефоне для управления вашим музыкальным проигрывателем..
Он имеет богатый и красивый интерфейс, который предназначен для сортировки музыки, управления радиостанциями, просмотра и подписки на подкасты.
При использовании с Mopidy вы можете слушать Spotify, которые помогут вам познакомиться с новой музыкой.
## Установка с GitHub
Инструкция [Новая Страница проекта](https://fatg3erman.github.io/RompR/)
![](docs/images/rompr-1.png)
![](docs/images/rompr-on-a-phone.png)

BIN
www/jukebox/REC/REC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

450
www/jukebox/REC/bootstrap.css vendored Normal file
View File

@ -0,0 +1,450 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
margin: 0;
*/
body {
margin-left:auto;
margin-right:auto;
width:98%;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main { /* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}

BIN
www/jukebox/REC/fond.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

View File

@ -0,0 +1,134 @@
<?php
/*
* THIS CODE IS RELATED TO zen/_CopyLaRadio shell's
DONE!!!? WRITE THAT CODE IN infobar2.js : arrount line 158 before "return lines;"
// ZEEBOX HACK ZONE
var host = window.location.hostname;
var reclink = 'http://' + host + ':10010/_CopyLaRadio/youtube-dl.php?q=REC&artist=' + escape(encodeURIComponent(npinfo.Artist)) + '&title=' + escape(encodeURIComponent(npinfo.Title)) + '&radio='+ escape(encodeURIComponent(npinfo.Album)) +'&maxResults=1';
var zcopylink = ' <a id="REC" title="Enregistrer sur Youtube" href="#" onclick=\'javascript:window.open("'+ reclink +'","REC","menubar=no, scrollbars=no, top=10, left=10, width=320, height=400");return false;\'>[REC]</a>';
zcopylink += ' <a title="RECHERCHE LIBRE" href="'+ reclink +'" target="youtube">*</a>';
lines[0].text += zcopylink;
// ZEEBOX HACK ZONE
*/
// RUN CLI if($argc>1) parse_str(implode('&',array_slice($argv, 1)), $_REQUEST);
$search=$radio=$artist=$title=$lnk=$lnkform=$cmd=$len="";
$search = trim(urldecode($_REQUEST['q']));
$radio = urldecode($_REQUEST['radio']);
$artist = urldecode($_REQUEST['artist']);
$title = urldecode($_REQUEST['title']);
$lnk = trim(urldecode($_REQUEST['lnk']));
$cmd="$radio|$artist|$title";
$len=strlen($artist.$title);
//$result='<a href="#null" onclick="javascript:window.close();"><img src="./REC.png"></a><br>';
$result='<a href="" target="copylaradio"><img src="./REC.png"></a><br>';
if( $radio == "" ) { $radio = "CopyLaRadio"; }
if( $radio == "Nova zz" ) {
$artist = "undefined";
$title = "undefined";
}
// Write request for copy.sh triggering
if( $search == "REC" ) {
$result.='<h2>♫ '.$radio.' ♫</h2>';
// LINK RECEIVED
if ($lnk) {
$artist="";
// Not making double request
if( ! exec('grep '.escapeshellarg($lnk).' /tmp/ytdl.list') ) {
// file_put_contents("/tmp/ytdl.list","CopyLibre||$lnk\n", FILE_APPEND);
// instead of using a relay daemon to monitor a /tmp shared file, we are going to send sbot message to make it. #zenyta SSB message.
shell_exec('/var/www/loveland/_CopyLaRadio/sbotc_zenyta.sh '. $lnk);
}
$result.='<p><a href="'.$lnk.'" target="check">LIEN: '.$lnk.'</a></p>';
// TRACK COPY (not for undefined or local file)
} else if ($radio && $artist != $title && $artist != "undefined" && $title != "undefined" && strlen(explode(".", $title)[1]) != 3 && explode(".", $title)[1] != "opus" ) {
// Not making double request
if( ! exec('grep '.escapeshellarg($cmd).' /tmp/ytdl.list') ) {
// shell_exec('/home/pi/G1sms+/_CopyLaRadio/parle.sh "Enregistrement ajouté."');
shell_exec('/var/www/loveland/_CopyLaRadio/artist_song_to_sbotc_zenyta.sh "'. $artist . '" "' . $title .'"');
} else {
shell_exec('/var/www/loveland/_CopyLaRadio/parle.sh "Copie déjà lancée"');
}
$result.='<a style="color:#FFFFFF" href="https://www.youtube.com/results?search_query='.urlencode($artist).'%20'.urlencode($title).'" target="check">
<h4>'.$artist.' / '.$title.'</h4>
</a>';
// RADIO EXTERNAL TRACK SCRAPERs ....
} else if($radio != "" && $artist == "undefined" && $title == "undefined" ){
shell_exec('/var/www/loveland/_CopyLaRadio/parle.sh "Recherche externe pour '.$radio.'"');
file_put_contents("/tmp/youtube-dl.log", "/home/pi/G1sms+/_CopyLaRadio/libradio/".escapeshellcmd($radio).".php".PHP_EOL, FILE_APPEND);
if ( file_exists("/home/pi/G1sms+/_CopyLaRadio/libradio/".escapeshellcmd($radio).".php") ) {
file_put_contents("/tmp/ytdl.list","$radio||".PHP_EOL, FILE_APPEND);
} else {
shell_exec('/var/www/loveland/_CopyLaRadio/parle.sh "Aucun module"');
}
} else {
shell_exec('/var/www/loveland/_CopyLaRadio/parle.sh "Identification imprécise. Podcast? Fichier local?"');
}
}
$ytform = '<form action="./youtube-dl.php" method="GET">
<div>
<h3>ARTISTE: <input type="search" id="artist" name="artist" size="17" placeholder="Indiquez un artiste" value="'.$artist.'"></h3>
<h3>TITRE: <input type="search" id="title" name="title" size="20" placeholder="Titre de la chanson" value="'.$title.'"></h3>
</div>
<input type="hidden" id="radio" name="radio" value="'.$radio.'">
<input type="hidden" id="q" name="q" value="REC">
<br>
<input type="submit" value="♫ Copie Youtube ♫">
</form>
';
$lnkform.='<form action="./youtube-dl.php" method="GET">
<a href="https://youtube.com" target="search"><img src="./youtube.png"></a>
<p><h1>youtube-dl</h1>
<a style="color:#FFFFFF" href="https://ytdl-org.github.io/youtube-dl/supportedsites.html" target="_blank">- Sites compatibles -</a>
<input type="search" id="lnk" name="lnk" placeholder="Inscrivez le lien à copier" value="" size="40">
<input type="hidden" id="q" name="q" value="REC">
</p>
<div>
<input type="submit" value="Copie du Lien #zenyta">
</div>
</form>';
?>
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="fred" >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<title><?php echo $search;?> - Recherche YouTube & Copie privée</title>
<link href="./bootstrap.css" rel="stylesheet">
</head>
<body style='background: url("./fond.jpg") no-repeat scroll center center / cover rgb(0, 0, 0); color:#FFFFFF;'>
<section class="content-section text-center">
<div class="container-fluid">
<div class="container" id="result" >
<div class="col-lg-8 col-lg-offset-2 page-scroll">
<ul>
<?php echo $result; ?>
<hr>
</ul>
<ul>
<?php echo $ytform; ?>
<hr>
<?php echo $lnkform; ?>
<hr>
</ul>
</div>
</div>
</div>
</section>
</body>
</html>

BIN
www/jukebox/REC/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

9
www/jukebox/_header Normal file
View File

@ -0,0 +1,9 @@
## Pages On This website
[Home](https://fatg3erman.github.io/RompR/)
[Recommended Linux Installation - with Nginx](https://fatg3erman.github.io/RompR/Recommended-Installation-on-Linux)
[Alternative Linux Installation - with Apache](https://fatg3erman.github.io/RompR/Installation-on-Linux-Alternative-Method)
[Troubleshooting](https://fatg3erman.github.io/RompR/Troubleshooting)

266
www/jukebox/albumart.php Normal file
View File

@ -0,0 +1,266 @@
<?php
define('ROMPR_IS_LOADING', true);
require_once ("includes/vars.php");
require_once ("includes/functions.php");
require_once ("international.php");
require_once ('utils/imagefunctions.php');
require_once ("backends/sql/backend.php");
require_once ("player/".$prefs['player_backend']."/player.php");
$only_plugins_on_menu = false;
$skin = "desktop";
set_version_string();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>RompЯ Album Art</title>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<?php
print '<script type="application/json" name="translations">'."\n".json_encode($translations)."\n</script>\n";
print '<script type="application/json" name="prefs">'."\n".json_encode($prefs)."\n</script>\n";
print '<link rel="stylesheet" type="text/css" href="css/layout-january.css?version=?'.ROMPR_VERSION.'" />'."\n";
print '<link rel="stylesheet" type="text/css" href="skins/desktop/skin.css?version=='.ROMPR_VERSION.'" />'."\n";
print '<link rel="stylesheet" type="text/css" href="css/albumart.css?version=?'.ROMPR_VERSION.'" />'."\n";
?>
<link rel="stylesheet" id="theme" type="text/css" />
<link type="text/css" href="css/jquery.mCustomScrollbar.css" rel="stylesheet" />
<?php
$scripts = array(
"jquery/jquery-3.3.1.min.js",
"jquery/jquery-migrate-3.0.1.js",
"ui/functions.js",
"ui/prefs.js",
"ui/language.js",
"jquery/jquery-ui.min-19.1.18.js",
"jquery/jquery.mCustomScrollbar.concat.min-3.1.5.js",
"includes/globals.js",
"ui/uifunctions.js",
"ui/metahandlers.js",
"ui/widgets.js",
"ui/debug.js",
"ui/coverscraper.js",
"ui/albumart.js"
);
foreach ($scripts as $i) {
logger::mark("INIT", "Loading ".$i);
print '<script type="text/javascript" src="'.$i.'?version='.ROMPR_VERSION.'"></script>'."\n";
}
include ("includes/globals.php");
?>
</head>
<body class="desktop">
<div id="pset" class="invisible"></div>
<div id="pmaxset" class="invisible"></div>
<div id="pbgset" class="invisible"></div>
<div class="albumcovers">
<div class="infosection">
<table width="100%">
<?php
print '<tr>
<td colspan="4"><h2>'.get_int_text("albumart_title").'</h2></td>
<td class="outer" align="right" colspan="1"><button id="finklestein">'.get_int_text("albumart_onlyempty").'</button></td>
</tr>';
print '<tr>
<td class="outer" id="totaltext"></td>
<td colspan="3"><div class="invisible" id="progress"></div></td>
<td class="outer" align="right"><button id="harold">'.get_int_text("albumart_getmissing").'</button></td>
</tr>';
// <td class="outer" align="right"><button id="doobag">'.get_int_text("albumart_findsmall").'</button></td>
print '<tr>
<td class="outer" id="infotext"></td>
<td colspan="3" align="center"><div class="inner" id="status">'.get_int_text('label_loading').'</div></td>
<td class="outer styledinputs" align="right"><input type="checkbox" class="topcheck" id="dinkytoys"><label for="dinkytoys" onclick="toggleLocal()">Ignore Local Images</label></td>
</tr>';
print '<tr>
<td colspan="4"></td>
<td class="outer styledinputs" align="right"><input type="checkbox" class="topcheck" id="poobag"><label for="poobag" onclick="toggleScrolling()">Follow Progress</label></td>
</tr>';
?>
</table>
</div>
</div>
<div id="wobblebottom">
<div id="artistcoverslist" class="tleft noborder">
<div class="noselection fullwidth">
<?php
if ($mysqlc) {
print '<div class="containerbox menuitem clickable clickselectartist selected" id="allartists"><div class="expand" class="artistrow">'.get_int_text("albumart_allartists").'</div></div>';
print '<div class="containerbox menuitem clickable clickselectartist" id="savedplaylists"><div class="expand" class="artistrow">Saved Playlists</div></div>';
print '<div class="containerbox menuitem clickable clickselectartist" id="radio"><div class="expand" class="artistrow">'.get_int_text("label_yourradio").'</div></div>';
do_artists_db_style();
}
?>
</div>
</div>
<div id="coverslist" class="tleft noborder">
<?php
// Do Local Albums
$count = 0;
$albums_without_cover = 0;
do_covers_db_style();
do_playlists();
do_radio_stations();
print '</div>';
print "</div>\n";
print "</div>\n";
print '<script language="JavaScript">'."\n";
print 'var numcovers = '.$count.";\n";
print 'var albums_without_cover = '.$albums_without_cover.";\n";
print "</script>\n";
print "</body>\n";
print "</html>\n";
function do_artists_db_style() {
$alist = get_list_of_artists();
foreach ($alist as $artist) {
print '<div class="containerbox menuitem clickable clickselectartist';
print '" id="artistname'.$artist['Artistindex'].'">';
print '<div class="expand" class="artistrow">'.$artist['Artistname'].'</div>';
print '</div>';
}
}
function do_covers_db_style() {
global $count;
global $albums_without_cover;
$alist = get_list_of_artists();
foreach ($alist as $artist) {
print '<div class="cheesegrater" name="artistname'.$artist['Artistindex'].'">';
print '<div class="albumsection">';
print '<div class="tleft"><h2>'.$artist['Artistname'].'</h2></div><div class="tright rightpad"><button class="invisible" onclick="getNewAlbumArt(\'#album'.$count.'\')">'.get_int_text("albumart_getthese").'</button></div>';
print "</div>\n";
print '<div id="album'.$count.'" class="containerbox fullwidth bigholder wrap">';
$blist = get_list_of_albums($artist['Artistindex']);
foreach ($blist as $album) {
print '<div class="fixed albumimg closet">';
print '<div class="covercontainer">';
$class = "clickable clickicon clickalbumcover droppable";
$src = "";
if ($album['Image'] && $album['Image'] !== "") {
$src = $album['Image'];
} else {
$class = $class . " notexist";
$albums_without_cover++;
}
print '<input name="albumpath" type="hidden" value="'.get_album_directory($album['Albumindex'], $album['AlbumUri']).'" />';
print '<input name="searchterm" type="hidden" value="'.rawurlencode($artist['Artistname']." ".munge_album_name($album['Albumname'])).'" />';
print '<img class="'.$class.'" name="'.$album['ImgKey'].'"';
if ($src != "") {
print ' src="'.$src.'" ';
}
print '/>';
print '<div>'.$album['Albumname'].'</div>';
print '</div>';
print '</div>';
$count++;
}
print "</div>\n";
print "</div>\n";
}
}
function do_radio_stations() {
global $count;
global $albums_without_cover;
$playlists = get_user_radio_streams();
if (count($playlists) > 0) {
print '<div class="cheesegrater" name="radio">';
print '<div class="albumsection">';
print '<div class="tleft"><h2>Radio Stations</h2></div><div class="tright rightpad"><button class="invisible" onclick="getNewAlbumArt(\'#album'.$count.'\')">'.get_int_text("albumart_getthese").'</button></div>';
print "</div>\n";
print '<div id="album'.$count.'" class="containerbox fullwidth bigholder wrap">';
foreach ($playlists as $file) {
print '<div class="fixed albumimg closet">';
print '<div class="covercontainer">';
$class = "";
$src = "";
if ($file['Image']) {
$src = $file['Image'];
} else {
$class = " notexist";
$albums_without_cover++;
}
print '<input name="searchterm" type="hidden" value="'.rawurlencode($file['StationName']).'" />';
print '<input name="artist" type="hidden" value="STREAM" />';
print '<input name="album" type="hidden" value="'.rawurlencode($file['StationName']).'" />';
$albumimage = new baseAlbumImage(array('artist' => 'STREAM', 'album' => $file['StationName']));
print '<img class="clickable clickicon clickalbumcover droppable'.$class.'" name="'.$albumimage->get_image_key().'"';
if ($src != "") {
print ' src="'.$src.'" ';
}
print '/>';
print '<div>'.htmlentities($file['StationName']).'</div>';
print '</div>';
print '</div>';
$count++;
}
print "</div>\n";
print "</div>\n";
}
}
function do_playlists() {
global $count;
global $albums_without_cover;
global $PLAYER_TYPE;
logger::log("PLAYLISTART", "Player type is", $PLAYER_TYPE);
$player = new $PLAYER_TYPE();
$playlists = $player->get_stored_playlists(false);
if (!is_array($playlists)) {
$playlists = array();
}
$plfiles = glob('prefs/userplaylists/*');
foreach ($plfiles as $f) {
$playlists[] = basename($f);
}
print '<div class="cheesegrater" name="savedplaylists">';
print '<div class="albumsection">';
print '<div class="tleft"><h2>Saved Playlists</h2></div>';
print "</div>\n";
print '<div id="album'.$count.'" class="containerbox fullwidth bigholder wrap">';
sort($playlists, SORT_STRING);
foreach ($playlists as $pl) {
logger::log("PLAYLISTART", "Playlist",$pl);
print '<div class="fixed albumimg closet">';
print '<div class="covercontainer">';
$class = "";
$albumimage = new baseAlbumImage(array('artist' => 'PLAYLIST', 'album' => $pl));
$src = $albumimage->get_image_if_exists();
if ($src === null) {
$class = " plimage notfound";
$src = '';
$albums_without_cover++;
}
$plsearch = preg_replace('/ \(by .*?\)$/', '', $pl);
print '<input name = "searchterm" type="hidden" value="'.rawurlencode($plsearch).'" />';
print '<input name="artist" type="hidden" value="PLAYLIST" />';
print '<input name="album" type="hidden" value="'.rawurlencode($pl).'" />';
print '<img class="clickable clickicon clickalbumcover droppable playlistimage'.$class.'" name="'.$albumimage->get_image_key().'"';
if ($src != "") {
print ' src="'.$src.'" ';
}
print '/>';
print '<div>'.htmlentities($pl).'</div>';
print '</div>';
print '</div>';
$count++;
}
print "</div>\n";
print "</div>\n";
}
?>

1
www/jukebox/albumart/.empty Executable file
View File

@ -0,0 +1 @@

331
www/jukebox/albums.php Normal file
View File

@ -0,0 +1,331 @@
<?php
// Automatic Collection Updates can be performed using cURL:
// curl -b "currenthost=Default;player_backend=mpd" http://localhost/rompr/albums.php?rebuild > /dev/null
// where currenthost is the name of one of the Players defined in the Configuration menu
// and player_backend MUST be mpd or mopidy, depending on what your player is.
// You can also use eg -b "debug_enabled=8;currenthost=MPD;player_backend=mpd"
// to get more debug info in the webserver error log.
require_once ("includes/vars.php");
require_once ("includes/functions.php");
require_once ("utils/imagefunctions.php");
require_once ("international.php");
require_once ("backends/sql/backend.php");
$error = 0;
logger::trace("TIMINGS", "======================================================================");
$initmem = memory_get_usage();
logger::trace("COLLECTION", "Memory Used is ".$initmem);
$now2 = time();
switch (true) {
case array_key_exists('item', $_REQUEST):
logit('item');
// Populate a dropdown in the collection or search results
dumpAlbums($_REQUEST['item']);
break;
case array_key_exists('mpdsearch', $_REQUEST):
logit('mpdsearch');
// Handle an mpd-style search request
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("collection/collection.php");
$trackbytrack = true;
$doing_search = true;
mpd_search();
break;
case array_key_exists('browsealbum', $_REQUEST):
logit('browsealbum');
// Populate a spotify album in mopidy's search results - as spotify doesn't return all tracks
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("collection/collection.php");
$trackbytrack = true;
$doing_search = true;
browse_album();
break;
case array_key_exists("rawterms", $_REQUEST):
logit('rawterms');
// Handle an mpd-style search request requiring tl_track format results
// Note that raw_search uses the collection models but not the database
// hence $trackbytrack must be false
logger::log("MPD SEARCH", "Doing RAW search");
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("collection/collection.php");
require_once ("collection/dbsearch.php");
$doing_search = true;
raw_search();
break;
case array_key_exists('terms', $_REQUEST):
logit('terms');
// SQL database search request
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("collection/collection.php");
require_once ("collection/dbsearch.php");
$doing_search = true;
database_search();
break;
case array_key_exists('rebuild', $_REQUEST):
logit('rebuild');
// This is a request to rebuild the music collection
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("collection/collection.php");
$trackbytrack = true;
update_collection();
break;
default:
logger::fail("ALBUMS", "Couldn't figure out what to do!");
break;
}
logger::trace("TIMINGS", "== Collection Update And Send took ".format_time(time() - $now2));
$peakmem = memory_get_peak_usage();
$ourmem = $peakmem - $initmem;
logger::trace("TIMINGS", "Peak Memory Used Was ".number_format($peakmem)." bytes - meaning we used ".number_format($ourmem)." bytes.");
logger::trace("TIMINGS", "======================================================================");
function logit($key) {
logger::log("COLLECTION", "Request is",$key,"=",$_REQUEST[$key]);
}
function checkDomains($d) {
if (array_key_exists('domains', $d)) {
return $d['domains'];
}
logger::debug("SEARCH", "No search domains in use");
return false;
}
function mpd_search() {
global $dbterms, $skin, $PLAYER_TYPE;
// If we're searching for tags or ratings it would seem sensible to only search the database
// HOWEVER - we could be searching for genre or performer or composer - which will not match in the database
// For those cases ONLY, controller.js will call into this instead of database_search, and we set $dbterms
// to make the collection check everything it finds against the database
$cmd = $_REQUEST['command'];
$domains = checkDomains($_REQUEST);
foreach ($_REQUEST['mpdsearch'] as $key => $term) {
switch ($key) {
case 'tag':
case 'rating':
$dbterms[$key] = $term;
break;
case 'any':
// This makes a search term of 'Madness My Girl' into
// search any Madness any My any Girl
// which seems to produce better results with Spotify. But probably doesn't with Google Play, which
// only uses the first term. Soundcloud concatenates them all back into one term again. What does MPD do?
foreach ($term as $t) {
$terms = explode(' ',$t);
foreach ($terms as $tom) {
$cmd .= " ".$key.' "'.format_for_mpd(html_entity_decode(trim($tom))).'"';
}
}
break;
default:
foreach ($term as $t) {
$cmd .= " ".$key.' "'.format_for_mpd(html_entity_decode(trim($t))).'"';
}
break;
}
}
logger::log("MPD SEARCH", "Search command : ".$cmd);
if ($_REQUEST['resultstype'] == "tree") {
require_once ("player/mpd/filetree.php");
require_once ("skins/".$skin."/ui_elements.php");
$player = new fileCollector();
$player->doFileSearch($cmd, $domains);
} else {
cleanSearchTables();
prepareCollectionUpdate();
$collection = new musicCollection();
$player = new $PLAYER_TYPE();
$player->populate_collection($cmd, $domains, $collection);
$collection->tracks_to_database();
close_transaction();
dumpAlbums($_REQUEST['dump']);
remove_findtracks();
}
}
function browse_album() {
global $PLAYER_TYPE, $skin;
$a = preg_match('/(a|b)(.*?)(\d+|root)/', $_REQUEST['browsealbum'], $matches);
if (!$a) {
print '<h3>'.get_int_text("label_general_error").'</h3>';
logger::error("DUMPALBUMS", "Browse Album Failed - regexp failed to match", $_REQUEST['browsealbum']);
return false;
}
$why = $matches[1];
$what = $matches[2];
$who = $matches[3];
$albumlink = get_albumlink($who);
if (substr($albumlink, 0, 8) == 'podcast+') {
require_once ('includes/podcastfunctions.php');
logger::log("ALBUMS", "Browsing For Podcast ".substr($albumlink, 9));
$podid = getNewPodcast(substr($albumlink, 8), 0, false);
logger::trace("ALBUMS", "Ouputting Podcast ID ".$podid);
outputPodcast($podid, false);
} else {
if (preg_match('/^.+?:artist:/', $albumlink)) {
remove_album_from_database($who);
}
$player = new $PLAYER_TYPE();
$collection = new musicCollection();
$cmd = 'find file "'.$albumlink.'"';
logger::log("MPD", "Doing Album Browse : ".$cmd);
prepareCollectionUpdate();
$player->populate_collection($cmd, false, $collection);
$collection->tracks_to_database(true);
close_transaction();
remove_findtracks();
if (preg_match('/^.+?:album:/', $albumlink)) {
// Just occasionally, the spotify album originally returned by search has an incorrect AlbumArtist
// When we browse the album the new tracks therefore get added to a new album, while the original tracks
// remain attached to the old one. This is where we use do_tracks_from_database with an array of albumids
// which joins them together into a virtual album, with the track ordering correct
print do_tracks_from_database($why, $what, find_justadded_albums(), true);
} else {
$artistarray = find_justadded_artists();
$do_controlheader = true;
foreach ($artistarray as $artistid) {
do_albums_from_database($why, 'album', $artistid, false, false, true, $do_controlheader);
$do_controlheader = false;
}
}
}
}
function raw_search() {
global $PLAYER_TYPE, $doing_search;
$domains = checkDomains($_REQUEST);
$collection = new musicCollection();
$found = 0;
logger::trace("MPD SEARCH", "checkdb is ".$_REQUEST['checkdb']);
if ($_REQUEST['checkdb'] !== 'false') {
logger::trace("MPD SEARCH", " ... checking database first ");
$found = doDbCollection($_REQUEST['rawterms'], $domains, "RAW", $collection);
if ($found > 0) {
logger::log("MPD SEARCH", " ... found ".$found." matches in database");
}
}
if ($found == 0) {
$cmd = $_REQUEST['command'];
foreach ($_REQUEST['rawterms'] as $key => $term) {
$cmd .= " ".$key.' "'.format_for_mpd(html_entity_decode($term[0])).'"';
}
logger::log("MPD SEARCH", "Search command : ".$cmd);
$doing_search = true;
$player = new $PLAYER_TYPE();
$player->populate_collection($cmd, $domains, $collection);
// For backends that don't support multiple parameters (Google Play)
// This'll return nothing for Spotify, so it's OK. It might help SoundCloud too.
$cmd = $_REQUEST['command'].' any ';
$parms = array();
if (array_key_exists('artist', $_REQUEST['rawterms'])) {
$parms[] = format_for_mpd(html_entity_decode($_REQUEST['rawterms']['artist'][0]));
}
if (array_key_exists('title', $_REQUEST['rawterms'])) {
$parms[] = format_for_mpd(html_entity_decode($_REQUEST['rawterms']['title'][0]));
}
if (count($parms) > 0) {
$cmd .= '"'.implode(' ',$parms).'"';
logger::log("MPD SEARCH", "Search command : ".$cmd);
$doing_search = true;
$collection->filter_duplicate_tracks();
$player->populate_collection($cmd, $domains, $collection);
}
}
print json_encode($collection->tracks_as_array());
}
function database_search() {
$tree = null;
$domains = checkDomains($_REQUEST);
if ($_REQUEST['resultstype'] == "tree") {
$tree = new mpdlistthing(null);
} else {
cleanSearchTables();
open_transaction();
}
$fcount = doDbCollection($_REQUEST['terms'], $domains, $_REQUEST['resultstype'], $tree);
if ($_REQUEST['resultstype'] == "tree") {
printFileSearch($tree, $fcount);
} else {
close_transaction();
dumpAlbums($_REQUEST['dump']);
}
}
function update_collection() {
global $PLAYER_TYPE;
// Check that an update is not currently in progress
// and create the update lock if not
if (collectionUpdateRunning()) {
header('HTTP/1.1 500 Internal Server Error');
print get_int_text('error_nocol');
exit(0);
}
if (file_exists('prefs/monitor')) {
unlink('prefs/monitor');
}
// Send some dummy data back to the browser, then close the connection
// so that the browser doesn't time out and retry
$sapi_type = php_sapi_name();
logger::log('COLLECTION','SAPI Name is',$sapi_type);
if (preg_match('/fpm/', $sapi_type) || preg_match('/fcgi/', $sapi_type)) {
logger::mark('COLLECTION', 'Closing Request The FastCGI Way');
print('<html></html>');
fastcgi_finish_request();
} else {
logger::mark('COLLECTION', 'Closing Request The Apache Way');
ob_end_clean();
ignore_user_abort(true); // just to be safe
ob_start();
print('<html></html>');
$size = ob_get_length();
header("Content-Length: $size");
header("Content-Encoding: none");
header("Connection: close");
ob_end_flush();
ob_flush();
flush();
if (ob_get_contents()) {
ob_end_clean();
}
}
if (session_id()) {
session_write_close();
}
// Browser is now happy. Now we can do our work in peace.
cleanSearchTables();
prepareCollectionUpdate();
$player = new $PLAYER_TYPE();
$player->musicCollectionUpdate();
tidy_database();
remove_findtracks();
// Add a marker to the monitor file to say we've finished
$player->collectionUpdateDone();
// Clear the update lock
clearUpdateLock();
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,301 @@
<?php
$mysqlc = null;
if (array_key_exists('collection_type', $prefs)) {
include("backends/sql/".$prefs['collection_type']."/specifics.php");
}
function probe_database() {
// In the case where collection_type is not set, probe to see which type of DB to use
// This keeps the behaviour the same as previous versions which auto-detected
// the database type. This does mean we get some duplicate code but this is
// so much better for the user.
global $mysqlc, $prefs;
logger::mark("SQL_CONNECT", "Probing Database Type");
logger::log("SQL_CONNECT", "Attempting to connect to MYSQL Server");
try {
if (is_numeric($prefs['mysql_port'])) {
logger::trace("SQL_CONNECT", "Connecting using hostname and port");
$dsn = "mysql:host=".$prefs['mysql_host'].";port=".$prefs['mysql_port'].";dbname=".$prefs['mysql_database'];
} else {
logger::trace("SQL_CONNECT", "Connecting using unix socket");
$dsn = "mysql:unix_socket=".$prefs['mysql_port'].";dbname=".$prefs['mysql_database'];
}
$mysqlc = new PDO($dsn, $prefs['mysql_user'], $prefs['mysql_password']);
logger::mark("SQL_CONNECT", "Connected to MySQL");
$prefs['collection_type'] = 'mysql';
} catch (Exception $e) {
logger::warn("SQL_CONNECT", "Couldn't connect to MySQL - ".$e);
$mysqlc = null;
}
if ($mysqlc == null) {
logger::log("SQL_CONNECT", "Attempting to use SQLite Database");
try {
$dsn = "sqlite:prefs/collection.sq3";
$mysqlc = new PDO($dsn);
logger::mark("MYSQL", "Connected to SQLite");
$prefs['collection_type'] = 'sqlite';
} catch (Exception $e) {
logger::fail("MYSQL", "Couldn't use SQLite Either - ".$e);
$mysqlc = null;
}
}
}
//
// Initialisation
//
function show_sql_error($text = "", $stmt = null) {
global $mysqlc;
logger::error("MYSQL ERROR", $text,":",$mysqlc->errorInfo()[1],":",$mysqlc->errorInfo()[2]);
if ($stmt !== null) {
logger::error("STMT ERROR", $text,":",$stmt->errorInfo()[1],":",$stmt->errorInfo()[2]);
}
}
//
// Queries
//
function generic_sql_query($qstring, $return_boolean = false, $return_type = PDO::FETCH_ASSOC, $return_value = null, $value_default = null, $return_rowcount = false ) {
global $mysqlc;
logger::debug("GENERIC_SQL", $qstring);
$retval = true;
if (($result = @$mysqlc->query($qstring)) !== false) {
logger::debug("GENERIC_SQL", "Done : ".($result->rowCount())." rows affected");
if ($return_value !== null) {
$arr = $result->fetch(PDO::FETCH_ASSOC);
$retval = ($arr) ? $arr[$return_value] : $value_default;
} else if ($return_boolean) {
$retval = true;
} else if ($return_rowcount) {
return $result->rowCount();
} else {
$retval = $result->fetchAll($return_type);
}
} else {
logger::warn("GENERIC_SQL", "Command Failed :",$qstring);
show_sql_error();
if ($return_value !== null) {
$retval = $value_default;
} else if ($return_boolean) {
$retval = false;
} else {
$retval = array();
}
}
$result = null;
return $retval;
}
function sql_get_column($qstring, $column) {
global $mysqlc;
logger::debug("SQL_GET_COLUMN", "Get column",$column,"from",$qstring);
$retval = array();
if (($result = $mysqlc->query($qstring)) !== false) {
$retval = $result->fetchAll(PDO::FETCH_COLUMN, $column);
}
return $retval;
}
function simple_query($select, $from, $where, $item, $default) {
$retval = $default;
$qstring = "SELECT ".$select." AS TheThingToFind FROM ".$from;
if ($where != null) {
$qstring .= " WHERE ".$where." = ?";
}
$retval = sql_prepare_query(false, null, 'TheThingToFind', $default, $qstring, $item);
return $retval;
}
function sql_prepare_query() {
// Variable arguments but at least 5 are required:
// 1. flag for whether to just return a boolean
// 2. return type
// 3. field name
// 4. default value for field name
// 5. query string
// ... parameters for query
// return type of PDO::FETCH_COLUMN returns an array of the values
// from the column identified by field name
// --**-- NO PARAMETER CHECKING IS DONE BY THIS FUNCTION! --**--
// because we want to make it fast, so make sure you call it right!
// This doesn't appear to work with MySQL when one of the args has to be an integer
// eg LIMIT ? doesn't work.
global $mysqlc;
$allargs = func_get_args();
logger::debug("SQL_PREPARE",$allargs);
$return_boolean = $allargs[0];
$return_type = $allargs[1];
$return_value = $allargs[2];
$value_default = $allargs[3];
$query = $allargs[4];
if (is_array($allargs[5])) {
$args = $allargs[5];
} else {
$args = array_slice($allargs, 5);
}
$stmt = $mysqlc->prepare($query);
if ($stmt !== false) {
if ($stmt->execute($args)) {
if ($return_type == PDO::FETCH_COLUMN) {
$retval = $stmt->fetchAll(PDO::FETCH_COLUMN, $return_value);
} else if ($return_value !== null) {
$arr = $stmt->fetch(PDO::FETCH_ASSOC);
$retval = ($arr) ? $arr[$return_value] : $value_default;
} else if ($return_boolean) {
$retval = true;
} else {
$retval = $stmt->fetchAll($return_type);
}
$stmt = null;
return $retval;
} else {
show_sql_error("SQL Statement Error for",$stmt);
}
} else {
show_sql_error();
}
if ($return_value !== null) {
$retval = $value_default;
} else if ($return_boolean) {
$retval = false;
} else {
$retval = array();
}
$stmt = null;
return $retval;
}
function sql_prepare_query_later($query) {
global $mysqlc;
$stmt = $mysqlc->prepare($query);
if ($stmt === FALSE) {
show_sql_error();
}
return $stmt;
}
// Debug function for prepared statement
function dbg_params($string,$data) {
$indexed = $data==array_values($data);
foreach($data as $k=>$v) {
if (is_string($v)) {
$v = "'$v'";
}
if($indexed) {
$string = preg_replace('/\?/', $v, $string, 1);
} else {
$string=str_replace(":$k", $v, $string);
}
}
return $string;
}
function checkCollectionStatus() {
$lv = generic_sql_query("SELECT Value FROM Statstable WHERE Item = 'ListVersion'", false, null, 'Value', null);
if ($lv == ROMPR_COLLECTION_VERSION) {
logger::log("MYSQL", "Collection version is correct");
return "0";
} else {
if ($lv > 0) {
logger::warn("MYSQL", "Collection version is outdated - ".$lv);
return "1";
} else {
logger::shout("MYSQL", "Collection has not been built".$lv);
return "2";
}
}
}
function checkAlbumArt() {
$oa = generic_sql_query("SELECT COUNT(ImgVersion) AS NumOldAlbums FROM Albumtable WHERE Image LIKE 'albumart/small/%' AND ImgVersion < ".ROMPR_IMAGE_VERSION, false, null, 'NumOldAlbums', 0);
logger::log("INIT", "There are ".$oa." albums with old-style album art");
return $oa;
}
function open_transaction() {
global $transaction_open, $mysqlc;
if (!$transaction_open) {
if ($mysqlc->beginTransaction()) {
$transaction_open = true;
}
}
}
function check_transaction() {
global $numdone, $transaction_open;
if ($transaction_open) {
if ($numdone >= ROMPR_MAX_TRACKS_PER_TRANSACTION) {
close_transaction();
open_transaction();
}
} else {
logger::warn("BACKEND", "WARNING! check_transaction called when transaction not open!");
}
}
function close_transaction() {
global $transaction_open, $numdone, $mysqlc;
if ($transaction_open) {
if ($mysqlc->commit()) {
$transaction_open = false;
$numdone = 0;
}
} else {
logger::warn("BACKEND", "WARNING! close_transaction called when transaction not open!");
}
}
function saveCollectionPlayer($type) {
global $prefs;
logger::mark("COLLECTION", "Setting Collection Type to",$type);
switch ($type) {
case 'mopidy':
sql_prepare_query(true, null, null, null,
"UPDATE Statstable SET Value = ? WHERE Item = 'CollType'", 1);
$prefs['collection_player'] = 'mopidy';
break;
case 'mpd':
sql_prepare_query(true, null, null, null,
"UPDATE Statstable SET Value = ? WHERE Item = 'CollType'", 0);
$prefs['collection_player'] = 'mpd';
break;
}
savePrefs();
}
function readCollectionPlayer($sp = treu) {
global $prefs;
$c = simple_query('Value', 'Statstable', 'Item', 'CollType', 999);
switch ($c) {
case 999:
logger::trace("COLLECTION", "Collection type from database is not set");
logger::trace("COLLECTION", "Prefs collection_player is currently",$prefs['collection_player']);
$prefs['collection_player'] = null;
break;
case 1:
logger::debug("COLLECTION", "Collection type from database is mopidy");
$prefs['collection_player'] = 'mopidy';
break;
case 0:
logger::debug("COLLECTION", "Collection type from database is mpd");
$prefs['collection_player'] = 'mpd';
break;
}
if ($sp) {
savePrefs();
}
return $c;
}
?>

View File

@ -0,0 +1,878 @@
<?php
class romprmetadata {
public static function sanitise_data(&$data) {
foreach (array( 'action',
'title',
'artist',
'trackno',
'duration',
'albumuri',
'image',
'album',
'uri',
'trackai',
'albumai',
'albumindex',
'searched',
'lastmodified',
'streamname',
'streamimage',
'streamuri',
'type',
'ambid',
'isaudiobook',
'attributes',
'imagekey',
'which',
'wltrack',
'reqid') as $key) {
if (!array_key_exists($key, $data)) {
$data[$key] = null;
}
}
foreach (array( 'trackno', 'duration', 'isaudiobook') as $key) {
if ($data[$key] == null) {
$data[$key] = 0;
}
}
$data['albumartist'] = array_key_exists('albumartist', $data) ? $data['albumartist'] : $data['artist'];
$data['date'] = (array_key_exists('date', $data) && $data['date'] != 0) ? getYear($data['date']) : null;
$data['urionly'] = array_key_exists('urionly', $data) ? true : false;
$data['disc'] = array_key_exists('disc', $data) ? $data['disc'] : 1;
$data['domain'] = array_key_exists('domain', $data) ? $data['domain'] : ($data['uri'] === null ? "local" : getDomain($data['uri']));
$data['hidden'] = 0;
$data['searchflag'] = 0;
if (substr($data['image'],0,4) == "http") {
$data['image'] = "getRemoteImage.php?url=".$data['image'];
}
if ($data['imagekey'] === null) {
$albumimage = new baseAlbumImage(array(
'artist' => artist_for_image($data['type'], $data['albumartist']),
'album' => $data['album']
));
$data['imagekey'] = $albumimage->get_image_key();
}
}
public static function set($data, $keep_wishlist = false) {
global $returninfo;
if ($data['artist'] === null ||
$data['title'] === null ||
$data['attributes'] == null) {
logger::error("USERRATING", "Something is not set", $data);
header('HTTP/1.1 400 Bad Request');
print json_encode(array('error' => 'Artist or Title or Attributes not set'));
exit(0);
}
switch ($data['artist']) {
case 'geturisfordir':
$ttids = romprmetadata::geturisfordir($data);
break;
case 'geturis':
$ttids = romprmetadata::geturis($data);
break;
default:
$ttids = romprmetadata::find_item($data, forcedUriOnly($data['urionly'], getDomain($data['uri'])));
break;
}
$newttids = array();
foreach ($ttids as $ttid) {
if ($keep_wishlist || !track_is_wishlist($ttid)) {
$newttids[] = $ttid;
}
}
$ttids = $newttids;
if (count($ttids) == 0) {
$ttids[0] = create_new_track($data);
logger::log("USERRATINGS", "Created New Track with TTindex ".$ttids[0]);
}
if (count($ttids) > 0) {
if (romprmetadata::doTheSetting($ttids, $data['attributes'], $data['uri'])) {
} else {
header('HTTP/1.1 417 Expectation Failed');
$returninfo['error'] = 'Setting attributes failed';
}
} else {
logger::fail("USERRATING", "TTID Not Found");
header('HTTP/1.1 417 Expectation Failed');
$returninfo['error'] = 'TTindex not found';
}
}
public static function add($data, $urionly = true) {
// This is used for adding specific tracks so we need urionly to be true
// We don't simply call into this using 'set' with urionly set to true
// because that might result in the rating being changed
// The only time we call inot this with $urionly set to false is when we're restoring a metadata
// backup. In that case we might be copying data from one setup to another and we might have
// the track already in local, so we don't want to add duplicates. Neither way is perfect but
// this makes most sense I think.
global $returninfo;
$ttids = romprmetadata::find_item($data, $urionly);
// As we check by URI we can only have one result.
$ttid = null;
if (count($ttids) > 0) {
$ttid = $ttids[0];
if (track_is_hidden($ttid) || track_is_searchresult($ttid)) {
logger::mark("USERRATINGS", "Track ".$ttid." being added is a search result or a hidden track");
// Setting attributes (Rating: 0) will unhide/un-searchify it. Ratings of 0 are got rid of
// by remove_cruft at the end, because they're meaningless
if ($data['attributes'] == null) {
$data['attributes'] = array(array('attribute' => 'Rating', 'value'=> 0));
}
} else {
logger::warn("USERRATINGS", "Track being added already exists");
}
}
check_for_wishlist_track($data);
if ($ttid == null) {
logger::log("USERRATINGS", "Creating Track being added");
$ttid = create_new_track($data);
}
romprmetadata::doTheSetting(array($ttid), $data['attributes'], $data['uri']);
}
public static function inc($data) {
global $returninfo;
// NOTE : 'inc' does not do what you might expect.
// This is not an 'increment' function, it still does a SET but it will create a hidden track
// if the track can't be found, compare to SET which creates a new unhidden track.
if ($data['artist'] === null ||
$data['title'] === null ||
$data['attributes'] == null) {
logger::error("USERRATING", "Something is not set",$data);
header('HTTP/1.1 400 Bad Request');
print json_encode(array('error' => 'Artist or Title or Attributes not set'));
exit(0);
}
$ttids = romprmetadata::find_item($data, forcedUriOnly(false,getDomain($data['uri'])));
if (count($ttids) == 0) {
logger::log("USERRATING", "Doing an INCREMENT action - Found NOTHING so creating hidden track");
$data['hidden'] = 1;
$ttids[0] = create_new_track($data);
}
romprmetadata::checkLastPlayed($data);
if (count($ttids) > 0) {
foreach ($ttids as $ttid) {
logger::trace("USERRATING", "Doing an INCREMENT action - Found TTID ",$ttid);
foreach ($data['attributes'] as $pair) {
logger::log("USERRATING", "(Increment) Setting",$pair["attribute"],"to",$pair["value"],"on",$ttid);
romprmetadata::increment_value($ttid, $pair["attribute"], $pair["value"], $data['lastplayed']);
}
$returninfo['metadata'] = get_all_data($ttid);
}
}
return $ttids;
}
private static function checkLastPlayed(&$data) {
if (array_key_exists('lastplayed', $data)) {
if (is_numeric($data['lastplayed'])) {
// Convert timestamp from LastFM into MySQL TIMESTAMP format
$data['lastplayed'] = date('Y-m-d H:i:s', $data['lastplayed']);
}
} else {
$data['lastplayed'] = date('Y-m-d H:i:s');
}
}
public static function syncinc($data) {
global $returninfo;
if ($data['artist'] === null ||
$data['title'] === null ||
$data['attributes'] == null) {
logger::error("SYNCINC", "Something is not set", $data);
header('HTTP/1.1 400 Bad Request');
print json_encode(array('error' => 'Artist or Title or Attributes not set'));
exit(0);
}
$ttids = romprmetadata::find_item($data, forcedUriOnly(false,getDomain($data['uri'])));
if (count($ttids) == 0) {
$ttids = romprmetadata::inc($data);
romprmetadata::resetSyncCounts($ttids);
return true;
}
romprmetadata::checkLastPlayed($data);
logger::log("SYNCINC", "LastPlayed is ".$data['lastplayed']);
foreach ($ttids as $ttid) {
logger::log("SYNCINC", "Doing a SYNC action on TTID ".$ttid);
$rowcount = generic_sql_query("UPDATE Playcounttable SET SyncCount = SyncCount - 1, LastPlayed = '".$data['lastplayed']."' WHERE TTindex = ".$ttid." AND SyncCount > 0",
false, null, null, null, true);
if ($rowcount > 0) {
logger::log("SYNCINC", " Decremented sync counter for this track");
} else {
$rowcount = generic_sql_query("UPDATE Playcounttable SET Playcount = Playcount + 1, LastPlayed = '".$data['lastplayed']."' WHERE TTindex = ".$ttid,
false, null, null, null, true);
if ($rowcount > 0) {
logger::log("SYNCINC", " Incremented Playcount for this track");
// At this point, SyncCount must have been zero but the update will have incremented it again,
// because of the trigger. resetSyncCounts takes care of this;
} else {
logger::log("SYNCINC", " Track not found in Playcounttable");
$metadata = get_all_data($ttid);
romprmetadata::increment_value($ttid, 'Playcount', $metadata['Playcount'] + 1, $data['lastplayed']);
// At this point, SyncCount must have been zero but the update will have incremented it again,
// because of the trigger. resetSyncCounts takes care of this;
}
romprmetadata::resetSyncCounts(array($ttid));
}
}
}
public static function resetSyncCounts($ttids) {
foreach ($ttids as $ttid) {
generic_sql_query("UPDATE Playcounttable SET SyncCount = 0 WHERE TTindex = ".$ttid, true);
}
}
public static function resetallsyncdata() {
generic_sql_query('UPDATE Playcounttable SET SyncCount = 0 WHERE TTindex > 0', true);
}
public static function remove($data) {
global $returninfo;
if ($data['artist'] === null || $data['title'] === null) {
header('HTTP/1.1 400 Bad Request');
print json_encode(array('error' => 'Artist or Title not set'));
exit(0);
}
$ttids = romprmetadata::find_item($data, forcedUriOnly($data['urionly'], getDomain($data['uri'])));
if (count($ttids) > 0) {
foreach ($ttids as $ttid) {
$result = true;
foreach ($data['attributes'] as $pair) {
logger::trace("USERRATING", "Removing",$pair);
$r = romprmetadata::remove_tag($ttid, $pair["value"]);
if ($r == false) {
logger::fail("USERRATING", "FAILED Removing",$pair);
$result = false;
}
}
if ($result) {
$returninfo['metadata'] = get_all_data($ttid);
} else {
header('HTTP/1.1 417 Expectation Failed');
$returninfo['error'] = 'Removing attributes failed';
}
}
} else {
logger::fail("USERRATING", "TTID Not Found");
header('HTTP/1.1 417 Expectation Failed');
$returninfo['error'] = 'TTindex not found';
}
}
public static function get($data) {
global $returninfo, $nodata;
if ($data['artist'] === null || $data['title'] === null) {
header('HTTP/1.1 400 Bad Request');
print json_encode(array('error' => 'Artist or Title not set'));
exit(0);
}
$ttids = romprmetadata::find_item($data, forcedUriOnly(false, getDomain($data['uri'])));
if (count($ttids) > 0) {
$ttid = array_shift($ttids);
$returninfo = get_all_data($ttid);
} else {
$returninfo = $nodata;
}
}
public static function setalbummbid($data) {
global $returninfo, $nodata;
$ttids = romprmetadata::find_item($data, forcedUriOnly(false, getDomain($data['uri'])));
if (count($ttids) > 0) {
foreach ($ttids as $ttid) {
logger::log("BACKEND", "Updating album MBID ".$data['attributes']." from TTindex ".$ttid);
$albumindex = simple_query('Albumindex', 'Tracktable', 'TTindex', $ttid, null);
logger::trace("BACKEND", " .. album index is ".$albumindex);
sql_prepare_query(true, null, null, null, "UPDATE Albumtable SET mbid = ? WHERE Albumindex = ? AND mbid IS NULL",$data['attributes'],$albumindex);
}
}
$returninfo = $nodata;
}
public static function cleanup($data) {
logger::log("SQL", "Doing Database Cleanup And Stats Update");
remove_cruft();
update_track_stats();
doCollectionHeader();
}
public static function amendalbum($data) {
if ($data['albumindex'] !== null && romprmetadata::amend_album($data['albumindex'], $data['albumartist'], $data['date'])) {
} else {
header('HTTP/1.1 400 Bad Request');
$returninfo['error'] = 'That just did not work';
}
}
public static function deletetag($data) {
if (romprmetadata::remove_tag_from_db($data['value'])) {
} else {
header('HTTP/1.1 400 Bad Request');
$returninfo['error'] = 'Well, that went well';
}
}
public static function delete($data) {
$ttids = romprmetadata::find_item($data, true);
if (count($ttids) == 0) {
header('HTTP/1.1 400 Bad Request');
$returninfo['error'] = 'TTindex not found';
} else {
romprmetadata::delete_track(array_shift($ttids));
}
}
public static function deletewl($data) {
romprmetadata::delete_track($data['wltrack']);
}
public static function deleteid($data) {
romprmetadata::delete_track($data['ttid']);
}
public static function getcharts($data) {
global $returninfo;
$returninfo['Artists'] = get_artist_charts();
$returninfo['Albums'] = get_album_charts();
$returninfo['Tracks'] = get_track_charts();
}
public static function clearwishlist() {
logger::log("MONKEYS", "Removing Wishlist Tracks");
if (clear_wishlist()) {
logger::log("MONKEYS", " ... Success!");
} else {
logger::warn("MONKEYS", "Failed removing wishlist tracks");
}
}
// Private Functions
static function geturisfordir($data) {
global $PLAYER_TYPE;
$player = new $PLAYER_TYPE();
$uris = $player->get_uris_for_directory($data['uri']);
$ttids = array();
foreach ($uris as $uri) {
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null, "SELECT TTindex FROM Tracktable WHERE Uri = ?", $uri);
$ttids = array_merge($ttids, $t);
}
return $ttids;
}
static function geturis($data) {
$uris = getItemsToAdd($data['uri'], "");
$ttids = array();
foreach ($uris as $uri) {
$uri = trim(substr($uri, strpos($uri, ' ')+1, strlen($uri)), '"');
$r = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null, "SELECT TTindex FROM Tracktable WHERE Uri = ?", $uri);
$ttids = array_merge($ttids, $t);
}
return $ttids;
}
static function print_debug_ttids($ttids, $s) {
$time = time() - $s;
if (count($ttids) > 0) {
logger::log("TIMINGS", " Found TTindex(es)",$ttids,"in",$time,"seconds");
}
}
static function find_item($data,$urionly) {
// romprmetadata::find_item
// Looks for a track in the database based on uri, title, artist, album, and albumartist or
// combinations of those
// Returns: Array of TTindex
// romprmetadata::find_item is used by userRatings to find tracks on which to update or display metadata.
// It is NOT used when the collection is created
// When Setting Metadata we do not use a URI because we might have mutliple versions of the
// track in the database or someone might be rating a track from Spotify that they already have
// in Local. So in this case we check using an increasingly wider check to find the track,
// returning as soon as one of these produces matches.
// First by Title, TrackNo, AlbumArtist and Album
// Third by Track, Album Artist, and Album
// Then by Track, Track Artist, and Album
// Then by Track, Artist, and Album NULL (meaning wishlist)
// We return ALL tracks found, because you might have the same track on multiple backends,
// and set metadata on them all.
// This means that when getting metadata it doesn't matter which one we match on.
// When we Get Metadata we do supply a URI BUT we don't use it if we have one, just because.
// $urionly can be set to force looking up only by URI. This is used by when we need to import a
// specific version of the track - currently from either the Last.FM importer or when we add a
// spotify album to the collection
// If we don't supply an album to this function that's because we're listening to the radio.
// In that case we look for a match where there is something in the album field and then for
// where album is NULL
// FIXME! There is one scenario where the above fails.
// If you tag or rate a track, and then add it to the collection again from another backend
// later on, the rating doesn't get picked up by the new copy.
// Looking everything up by name/album/artist (i.e. ignoring the URI in romprmetadata::find_item)
// doesn't fix this because the collection display still doesn't show the rating as that's
// looked up by TTindex
$start_time = time();
logger::shout("FIND ITEM", "Looking for item ".$data['title']);
$ttids = array();
if ($urionly && $data['uri']) {
logger::mark("FIND ITEM", " Trying by URI ".$data['uri']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null, "SELECT TTindex FROM Tracktable WHERE Uri = ?", $data['uri']);
$ttids = array_merge($ttids, $t);
}
if ($data['artist'] == null || $data['title'] == null || ($urionly && $data['uri'])) {
romprmetadata::print_debug_ttids($ttids, $start_time);
return $ttids;
}
if (count($ttids) == 0) {
if ($data['album']) {
if ($data['albumartist'] !== null && $data['trackno'] != 0) {
logger::mark("FIND ITEM", " Trying by albumartist",$data['albumartist'],"album",$data['album'],"title",$data['title'],"track number",$data['trackno']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Albumtable USING (Albumindex)
JOIN Artisttable ON Albumtable.AlbumArtistindex = Artisttable.Artistindex
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND LOWER(Albumname) = LOWER(?)
AND TrackNo = ?",
$data['title'], $data['albumartist'], $data['album'], $data['trackno']);
$ttids = array_merge($ttids, $t);
}
if (count($ttids) == 0 && $data['albumartist'] !== null) {
logger::mark("FIND ITEM", " Trying by albumartist",$data['albumartist'],"album",$data['album'],"and title",$data['title']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Albumtable USING (Albumindex)
JOIN Artisttable ON Albumtable.AlbumArtistindex = Artisttable.Artistindex
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND LOWER(Albumname) = LOWER(?)",
$data['title'], $data['albumartist'], $data['album']);
$ttids = array_merge($ttids, $t);
}
if (count($ttids) == 0 && ($data['albumartist'] == null || $data['albumartist'] == $data['artist'])) {
logger::mark("FIND ITEM", " Trying by artist",$data['artist'],",album",$data['album'],"and title",$data['title']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Artisttable USING (Artistindex)
JOIN Albumtable USING (Albumindex)
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND LOWER(Albumname) = LOWER(?)", $data['title'], $data['artist'], $data['album']);
$ttids = array_merge($ttids, $t);
}
// Finally look for Uri NULL which will be a wishlist item added via a radio station
if (count($ttids) == 0) {
logger::mark("FIND ITEM", " Trying by (wishlist) artist",$data['artist'],"and title",$data['title']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Artisttable USING (Artistindex)
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND Uri IS NULL",
$data['title'], $data['artist']);
$ttids = array_merge($ttids, $t);
}
} else {
// No album supplied - ie this is from a radio stream. First look for a match where
// there is something in the album field
logger::mark("FIND ITEM", " Trying by artist",$data['artist'],"Uri NOT NULL and title",$data['title']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Artisttable USING (Artistindex)
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND Uri IS NOT NULL", $data['title'], $data['artist']);
$ttids = array_merge($ttids, $t);
if (count($ttids) == 0) {
logger::mark("FIND ITEM", " Trying by (wishlist) artist",$data['artist'],"and title",$data['title']);
$t = sql_prepare_query(false, PDO::FETCH_COLUMN, 'TTindex', null,
"SELECT
TTindex
FROM
Tracktable JOIN Artisttable USING (Artistindex)
WHERE
LOWER(Title) = LOWER(?)
AND LOWER(Artistname) = LOWER(?)
AND Uri IS NULL", $data['title'], $data['artist']);
$ttids = array_merge($ttids, $t);
}
}
}
romprmetadata::print_debug_ttids($ttids, $start_time);
return $ttids;
}
static function increment_value($ttid, $attribute, $value, $lp) {
// Increment_value doesn't 'increment' as such - it's used for setting values on tracks without
// unhiding them. It's used for Playcount, which was originally an 'increment' type function but
// that changed because multiple rompr instances cause multiple increments
logger::mark("INCREMENT", "Setting",$attribute,"to",$value,"for TTID",$ttid);
if (sql_prepare_query(true, null, null, null, "REPLACE INTO ".$attribute."table (TTindex, ".$attribute.", LastPlayed) VALUES (?, ?, ?)", $ttid, $value, $lp)) {
logger::trace("INCREMENT", " .. success");
} else {
logger::fail("INCREMENT", "FAILED Setting",$attribute,"to",$value,"for TTID",$ttid);
return false;
}
return true;
}
static function set_attribute($ttid, $attribute, $value) {
// set_attribute
// Sets an attribute (Rating, Tag etc) on a TTindex.
logger::mark("ATTRIBUTE", "Setting",$attribute,"to",$value,"on",$ttid);
if (sql_prepare_query(true, null, null, null, "REPLACE INTO ".$attribute."table (TTindex, ".$attribute.") VALUES (?, ?)", $ttid, $value)) {
logger::trace("ATTRIBUTE", " .. success");
} else {
logger::fail("ATTRIBUTE", "FAILED Setting",$attribute,"to",$value,"on",$ttid);
return false;
}
return true;
}
static function doTheSetting($ttids, $attributes, $uri) {
global $returninfo;
$result = true;
logger::trace("USERRATING", "Checking For attributes");
if ($attributes !== null) {
logger::trace("USERRATING", "Setting attributes");
foreach($ttids as $ttid) {
logger::debug("USERRATING", "TTid ".$ttid);
foreach ($attributes as $pair) {
logger::log("USERRATING", "Setting",$pair["attribute"],"to",$pair['value'],"on TTindex",$ttid);
switch ($pair['attribute']) {
case 'Tags':
$result = romprmetadata::addTags($ttid, $pair['value']);
break;
default:
$result = romprmetadata::set_attribute($ttid, $pair["attribute"], $pair["value"]);
break;
}
if (!$result) { break; }
}
if ($uri) {
$returninfo['metadata'] = get_all_data($ttid);
}
}
}
return $result;
}
static function addTags($ttid, $tags) {
// addTags
// Add a list of tags to a TTindex
foreach ($tags as $tag) {
$t = trim($tag);
if ($t == '') continue;
logger::mark("ADD TAGS", "Adding Tag",$t,"to TTindex",$ttid);
$tagindex = sql_prepare_query(false, null, 'Tagindex', null, "SELECT Tagindex FROM Tagtable WHERE Name=?", $t);
if ($tagindex == null) $tagindex = romprmetadata::create_new_tag($t);
if ($tagindex == null) {
logger::fail("ADD TAGS", " Could not create tag",$t);
return false;
}
if ($result = generic_sql_query("INSERT INTO TagListtable (TTindex, Tagindex) VALUES ('".$ttid."', '".$tagindex."')", true)) {
logger::trace("ADD TAGS", "Success");
} else {
// Doesn't matter, we have a UNIQUE constraint on both columns to prevent us adding the same tag twice
logger::debug("ADD TAGS", " .. Failed but that's OK if it's because of a duplicate entry or UNQIUE constraint");
}
}
return true;
}
static function create_new_tag($tag) {
// create_new_tags
// Creates a new entry in Tagtable
// Returns: Tagindex
global $mysqlc;
logger::mark("CREATE TAG", "Creating new tag",$tag);
$tagindex = null;
if (sql_prepare_query(true, null, null, null, "INSERT INTO Tagtable (Name) VALUES (?)", $tag)) {
$tagindex = $mysqlc->lastInsertId();
}
return $tagindex;
}
static function remove_tag($ttid, $tag) {
// remove_tags
// Removes a tag relation from a TTindex
logger::mark("REMOVE TAG", "Removing Tag",$tag,"from TTindex",$ttid);
$retval = false;
if ($tagindex = simple_query('Tagindex', 'Tagtable', 'Name', $tag, false)) {
$retval = generic_sql_query("DELETE FROM TagListtable WHERE TTindex = '".$ttid."' AND Tagindex = '".$tagindex."'", true);
} else {
logger::fail("REMOVE TAG", " .. Could not find tag",$tag);
}
return $retval;
}
static function remove_tag_from_db($tag) {
logger::mark("REMOVE TAG", "Removing Tag",$tag,",from database");
return sql_prepare_query(true, null, null, null, "DELETE FROM Tagtable WHERE Name=?", $tag);
}
static function delete_track($ttid) {
if (remove_ttid($ttid)) {
} else {
header('HTTP/1.1 400 Bad Request');
}
}
static function amend_album($albumindex, $newartist, $date) {
logger::mark("AMEND ALBUM", "Updating Album index",$albumindex,"with new artist",$newartist,"and new date",$date);
$artistindex = ($newartist == null) ? null : check_artist($newartist);
$result = sql_prepare_query(false, PDO::FETCH_OBJ, null, null, "SELECT * FROM Albumtable WHERE Albumindex = ?", $albumindex);
$obj = array_shift($result);
if ($obj) {
$params = array(
'album' => $obj->Albumname,
'albumai' => ($artistindex == null) ? $obj->AlbumArtistindex : $artistindex,
'albumuri' => $obj->AlbumUri,
'image' => $obj->Image,
'date' => ($date == null) ? $obj->Year : $date,
'searched' => $obj->Searched,
'imagekey' => $obj->ImgKey,
'ambid' => $obj->mbid,
'domain' => $obj->Domain);
$newalbumindex = check_album($params);
if ($albumindex != $newalbumindex) {
logger::log("AMEND ALBUM", "Moving all tracks from album",$albumindex,"to album",$newalbumindex);
if (sql_prepare_query(true, null, null, null, "UPDATE Tracktable SET Albumindex = ? WHERE Albumindex = ?", $newalbumindex, $albumindex)) {
logger::trace("AMEND ALBUM", "...Success");
} else {
logger::fail("AMEND ALBUM", "Track move Failed!");
return false;
}
}
} else {
logger::error("AMEND ALBUM", "Failed to find album to update!");
return false;
}
return true;
}
}
function forcedUriOnly($u,$d) {
// Some mopidy backends - YouTube and SoundCloud - can return the same artist/album/track info
// for multiple different tracks.
// This gives us a problem because romprmetadata::find_item will think they're the same.
// So for those backends we always force urionly to be true
logger::debug("USERRATINGS", "Checking domain : ".$d);
if ($u || $d == "youtube" || $d == "soundcloud") {
return true;
} else {
return false;
}
}
function preparePlaylist() {
generic_sql_query("DROP TABLE IF EXISTS pltable", true);
generic_sql_query("CREATE TABLE pltable(TTindex INT UNSIGNED NOT NULL UNIQUE)", true);
}
function preparePlTrackTable() {
generic_sql_query("DROP TABLE IF EXISTS pltracktable", true);
generic_sql_query("CREATE TABLE pltracktable(TTindex INT UNSIGNED NOT NULL UNIQUE)", true);
}
function doPlaylist($playlist, $limit) {
global $prefs;
logger::blurt("SMARTRADIO", "Loading Playlist",$playlist,'limit',$limit);
$sqlstring = "";
$tags = null;
$random = true;
switch($playlist) {
case "1stars":
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Ratingtable USING (TTindex) WHERE Uri
IS NOT NULL AND Hidden=0 AND isSearchResult < 2 AND Rating > 0";
break;
case "2stars":
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Ratingtable USING (TTindex) WHERE Uri
IS NOT NULL AND Hidden=0 AND isSearchResult < 2 AND Rating > 1";
break;
case "3stars":
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Ratingtable USING (TTindex) WHERE Uri
IS NOT NULL AND Hidden=0 AND isSearchResult < 2 AND Rating > 2";
break;
case "4stars":
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Ratingtable USING (TTindex) WHERE Uri
IS NOT NULL AND Hidden=0 AND isSearchResult < 2 AND Rating > 3";
break;
case "5stars":
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Ratingtable USING (TTindex) WHERE Uri
IS NOT NULL AND Hidden=0 AND isSearchResult < 2 AND Rating > 4";
break;
case "favealbums":
case "recentlyadded_byalbum":
// This is a rather odd SQL query but it needs a WHERE clause and a JOIN with Tracktable
// in order to work with the generic track dumping functions
$sqlstring = "SELECT TTindex FROM pltracktable JOIN Tracktable USING (TTindex) WHERE TTindex > 0";
$random = false;
break;
case "recentlyadded_random":
$sqlstring = "SELECT TTindex FROM pltracktable JOIN Tracktable USING (TTindex) WHERE TTindex > 0";
break;
case "mostplayed":
// Used to be tracks with above average playcount, now also includes any rated tracks.
// Still called mostplayed :)
$avgplays = getAveragePlays();
$sqlstring = "SELECT TTindex FROM Tracktable JOIN Playcounttable USING (TTindex)
LEFT JOIN Ratingtable USING (TTindex) WHERE Uri IS NOT NULL AND Hidden = 0 AND
isSearchResult < 2 AND (Playcount > ".$avgplays." OR Rating IS NOT NULL)";
break;
case "allrandom":
$sqlstring = "SELECT TTindex FROM Tracktable WHERE Uri IS NOT NULL AND Hidden=0 AND
isSearchResult < 2";
break;
case "neverplayed":
// LEFT JOIN (used here and above) means that the right-hand side of the JOIN will be
// NULL if TTindex doesn't exist on that side. Very handy.
$sqlstring = "SELECT Tracktable.TTindex FROM Tracktable LEFT JOIN Playcounttable ON
Tracktable.TTindex = Playcounttable.TTindex WHERE Playcounttable.TTindex IS NULL";
break;
case "recentlyplayed":
$sqlstring = recently_played_playlist();
break;
default:
if (preg_match('/tag\+(.*)/', $playlist, $matches)) {
$taglist = explode(',', $matches[1]);
$sqlstring = 'SELECT DISTINCT TTindex FROM Tracktable JOIN TagListtable USING (TTindex) JOIN Tagtable USING (Tagindex) WHERE ';
// Concatenate this bracket here otherwise Atom's syntax colouring goes haywire
$sqlstring .= '(';
$tags = array();
foreach ($taglist as $i => $tag) {
logger::mark("SMART RADIO", "Getting tag playlist for",$tag);
$tags[] = trim($tag);
if ($i > 0) {
$sqlstring .= " OR ";
}
$sqlstring .= "Tagtable.Name = ?";
}
$sqlstring .= ") AND Tracktable.Uri IS NOT NULL AND Tracktable.Hidden = 0 AND
Tracktable.isSearchResult < 2 ";
} else {
logger::fail("SMART RADIO", "Unrecognised playlist",$playlist);
}
break;
}
$sqlstring .= ' AND (LinkChecked = 0 OR LinkChecked = 2) AND isAudiobook = 0';
if ($prefs['collection_player'] == 'mopidy' && $prefs['player_backend'] == 'mpd') {
$sqlstring .= ' AND Uri LIKE "local:%"';
}
$uris = getAllURIs($sqlstring, $limit, $tags, $random);
$json = array();
foreach ($uris as $u) {
$json[] = array( 'type' => 'uri', 'name' => $u);
}
return $json;
}
function getAllURIs($sqlstring, $limit, $tags, $random = true) {
// Get all track URIs using a supplied SQL string. For playlist generators
$uris = array();
$tries = 0;
do {
if ($tries == 1) {
logger::log("SMART PLAYLIST", "No URIs found. Resetting history table");
preparePlaylist();
}
generic_sql_query("CREATE TEMPORARY TABLE IF NOT EXISTS pltemptable(TTindex INT UNSIGNED NOT NULL UNIQUE)", true);
theBabyDumper($sqlstring, $limit, $tags, $random);
$uris = sql_get_column("SELECT Uri FROM Tracktable WHERE TTindex IN (SELECT TTindex FROM pltemptable)", 0);
$tries++;
} while (count($uris) == 0 && $tries < 2);
generic_sql_query("INSERT INTO pltable (TTindex) SELECT TTindex FROM pltemptable", true);
return $uris;
}
function theBabyDumper($sqlstring, $limit, $tags, $random) {
logger::trace("SMART PLAYLIST", "Selector is ".$sqlstring);
$rndstr = $random ? " ORDER BY ".SQL_RANDOM_SORT : " ORDER BY Albumindex, TrackNo";
if ($tags) {
sql_prepare_query(true, null, null, null,
"INSERT INTO pltemptable(TTindex) ".$sqlstring.
" AND NOT Tracktable.TTindex IN (SELECT TTindex FROM pltable)".$rndstr." LIMIT ".$limit, $tags);
} else {
generic_sql_query(
"INSERT INTO pltemptable(TTindex) ".$sqlstring.
" AND NOT Tracktable.TTindex IN (SELECT TTindex FROM pltable)".$rndstr." LIMIT ".$limit, true);
}
}
function getAveragePlays() {
$avgplays = simple_query('avg(Playcount)', 'Playcounttable', null, null, 0);
return round($avgplays, 0, PHP_ROUND_HALF_DOWN);
}
?>

View File

@ -0,0 +1,960 @@
<?php
define('SQL_RANDOM_SORT', 'RAND()');
define('SQL_TAG_CONCAT', "GROUP_CONCAT(t.Name SEPARATOR ', ') ");
function connect_to_database($sp = true) {
global $mysqlc, $prefs;
if ($mysqlc !== null) {
logger::error("MYSQL", "AWOOOGA! ATTEMPTING MULTIPLE DATABASE CONNECTIONS!");
return;
}
try {
if (is_numeric($prefs['mysql_port'])) {
logger::debug("SQL_CONNECT", "Connecting using hostname and port");
$dsn = "mysql:host=".$prefs['mysql_host'].";port=".$prefs['mysql_port'].";dbname=".$prefs['mysql_database'];
} else {
logger::debug("SQL_CONNECT", "Connecting using unix socket");
$dsn = "mysql:unix_socket=".$prefs['mysql_port'].";dbname=".$prefs['mysql_database'];
}
$mysqlc = new PDO($dsn, $prefs['mysql_user'], $prefs['mysql_password']);
logger::debug("SQL_CONNECT", "Connected to MySQL");
generic_sql_query("SET NAMES utf8", true);
generic_sql_query('SET SESSION sql_mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"', true);
readCollectionPlayer($sp);
} catch (Exception $e) {
logger::fail("SQL_CONNECT", "Database connect failure - ".$e);
sql_init_fail($e->getMessage());
}
}
function close_database() {
global $mysqlc;
$mysqlc = null;
}
function check_sql_tables() {
global $mysqlc, $prefs;
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Tracktable(".
"TTindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"PRIMARY KEY(TTindex), ".
"Title VARCHAR(255), ".
"Albumindex INT UNSIGNED, ".
"TrackNo SMALLINT UNSIGNED, ".
"Duration INT UNSIGNED, ".
"Artistindex INT UNSIGNED, ".
"Disc TINYINT(3) UNSIGNED, ".
"Uri VARCHAR(2000), ".
"LastModified CHAR(32), ".
"Hidden TINYINT(1) UNSIGNED DEFAULT 0, ".
"DateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ".
"isSearchResult TINYINT(1) UNSIGNED DEFAULT 0, ".
"justAdded TINYINT(1) UNSIGNED DEFAULT 1, ".
"Sourceindex INT UNSIGNED DEFAULT NULL, ".
"LinkChecked TINYINT(1) UNSIGNED DEFAULT 0, ".
"isAudiobook TINYINT(1) UNSIGNED DEFAULT 0, ".
"INDEX(Albumindex), ".
"INDEX(Title), ".
"INDEX(TrackNo)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Tracktable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Albumtable(".
"Albumindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"PRIMARY KEY(Albumindex), ".
"Albumname VARCHAR(255), ".
"AlbumArtistindex INT UNSIGNED, ".
"AlbumUri VARCHAR(255), ".
"Year YEAR, ".
"Searched TINYINT(1) UNSIGNED, ".
"ImgKey CHAR(32), ".
"mbid CHAR(40), ".
"ImgVersion INT UNSIGNED DEFAULT ".ROMPR_IMAGE_VERSION.", ".
"Domain CHAR(32), ".
"Image VARCHAR(255), ".
"justUpdated TINYINT(1) UNSIGNED DEFAULT 1, ".
"INDEX(Albumname), ".
"INDEX(AlbumArtistindex), ".
"INDEX(Domain), ".
"INDEX(ImgKey)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Albumtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Artisttable(".
"Artistindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"PRIMARY KEY(Artistindex), ".
"Artistname VARCHAR(255), ".
"INDEX(Artistname)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Artisttable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Artisttable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Ratingtable(".
"TTindex INT UNSIGNED, ".
"PRIMARY KEY(TTindex), ".
"Rating TINYINT(1) UNSIGNED) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Ratingtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Ratingtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Progresstable(".
"TTindex INT UNSIGNED, ".
"PRIMARY KEY(TTindex), ".
"Progress INT UNSIGNED) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Progresstable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Progresstable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Tagtable(".
"Tagindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"PRIMARY KEY(Tagindex), ".
"Name VARCHAR(255)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Tagtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tagtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS TagListtable(".
"Tagindex INT UNSIGNED NOT NULL REFERENCES Tagtable(Tagindex), ".
"TTindex INT UNSIGNED NOT NULL REFERENCES Tracktable(TTindex), ".
"PRIMARY KEY (Tagindex, TTindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " TagListtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking TagListtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Playcounttable(".
"TTindex INT UNSIGNED NOT NULL REFERENCES Tracktable(TTindex), ".
"Playcount INT UNSIGNED NOT NULL, ".
"SyncCount INT UNSIGNED DEFAULT 0, ".
"LastPlayed TIMESTAMP, ".
"PRIMARY KEY (TTindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Playcounttable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Playcounttable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Podcasttable(".
"PODindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"FeedURL TEXT, ".
"LastUpdate INT UNSIGNED, ".
"Image VARCHAR(255), ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"RefreshOption TINYINT(2) UNSIGNED DEFAULT 0, ".
"SortMode TINYINT(2) UNSIGNED DEFAULT 0, ".
"HideDescriptions TINYINT(1) UNSIGNED DEFAULT 0, ".
"DisplayMode TINYINT(2) UNSIGNED DEFAULT 0, ".
"DaysToKeep INT UNSIGNED DEFAULT 0, ".
"NumToKeep INT UNSIGNED DEFAULT 0, ".
"KeepDownloaded TINYINT(1) UNSIGNED DEFAULT 0, ".
"AutoDownload TINYINT(1) UNSIGNED DEFAULT 0, ".
"DaysLive INT, ".
"Version TINYINT(2), ".
"Subscribed TINYINT(1) NOT NULL DEFAULT 1, ".
"Description TEXT, ".
"LastPubDate INT UNSIGNED DEFAULT NULL, ".
"Category VARCHAR(255) NOT NULL, ".
"PRIMARY KEY (PODindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " Podcasttable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Podcasttable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS PodcastTracktable(".
"PODTrackindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"JustUpdated TINYINT(1), ".
"PODindex INT UNSIGNED, ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"Duration INT UNSIGNED, ".
"PubDate INT, ".
"FileSize INT UNSIGNED, ".
"Description TEXT, ".
"Link TEXT, ".
"Guid TEXT, ".
"Localfilename VARCHAR(255), ".
"Downloaded TINYINT(1) UNSIGNED DEFAULT 0, ".
"Listened TINYINT(1) UNSIGNED DEFAULT 0, ".
"New TINYINT(1) UNSIGNED DEFAULT 1, ".
"Deleted TINYINT(1) UNSIGNED DEFAULT 0, ".
"Progress INT UNSIGNED DEFAULT 0, ".
"INDEX (PODindex), ".
"PRIMARY KEY (PODTrackindex), ".
"INDEX (Title)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " PodcastTracktable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking PodcastTracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS RadioStationtable(".
"Stationindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"Number SMALLINT UNSIGNED DEFAULT 65535, ".
"IsFave TINYINT(1), ".
"StationName VARCHAR(255), ".
"PlaylistUrl TEXT, ".
"Image VARCHAR(255), ".
"PRIMARY KEY (Stationindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " RadioStationtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioStationtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS RadioTracktable(".
"Trackindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"Stationindex INT UNSIGNED REFERENCES RadioStationtable(Stationindex), ".
"TrackUri TEXT, ".
"PrettyStream TEXT, ".
"PRIMARY KEY (Trackindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " RadioTracktable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioTracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS WishlistSourcetable(".
"Sourceindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"SourceName VARCHAR(255), ".
"SourceImage VARCHAR(255), ".
"SourceUri TEXT, ".
"PRIMARY KEY (Sourceindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " WishlistSourcetable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking WishlistSourcetable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS AlbumsToListenTotable(".
"Listenindex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"JsonData TEXT, ".
"PRIMARY KEY (Listenindex)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " AlbumsToListenTotabletable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking AlbumsToListenTotable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS BackgroundImageTable(".
"BgImageIndex INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, ".
"Skin VARCHAR(255), ".
"BrowserID VARCHAR(20) DEFAULT NULL, ".
"Filename VARCHAR(255), ".
"Orientation TINYINT(2), ".
"PRIMARY KEY (BgImageIndex), ".
"INDEX (Skin), ".
"INDEX (BrowserID)) ENGINE=InnoDB", true))
{
logger::log("MYSQL_CONNECT", " BackgounrdImageTable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking BackgroundImageTable : ".$err);
}
if (!generic_sql_query("CREATE TABLE IF NOT EXISTS Statstable(Item CHAR(11), PRIMARY KEY(Item), Value INT UNSIGNED) ENGINE=InnoDB", true)) {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Statstable : ".$err);
}
// Check schema version and update tables as necessary
$sv = simple_query('Value', 'Statstable', 'Item', 'SchemaVer', 0);
if ($sv == 0) {
logger::log("SQL_CONNECT", "No Schema Version Found - initialising table");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('ListVersion', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('ArtistCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('AlbumCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('TrackCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('TotalTime', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('CollType', '999')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('SchemaVer', '".ROMPR_SCHEMA_VERSION."')", true);
$sv = ROMPR_SCHEMA_VERSION;
logger::log("MYSQL_CONNECT", "Statstable populated");
create_update_triggers();
create_conditional_triggers();
create_playcount_triggers();
}
if ($sv > ROMPR_SCHEMA_VERSION) {
logger::log("MYSQL_CONNECT", "Schema Mismatch! We are version ".ROMPR_SCHEMA_VERSION." but database is version ".$sv);
return array(false, "Your database has version number ".$sv." but this version of rompr only handles version ".ROMPR_SCHEMA_VERSION);
}
while ($sv < ROMPR_SCHEMA_VERSION) {
switch ($sv) {
case 0:
logger::error("SQL", "BIG ERROR! No Schema Version found!!");
return array(false, "Database Error - could not read schema version. Cannot continue.");
break;
case 1:
logger::log("SQL", "Updating FROM Schema version 1 TO Schema version 2");
generic_sql_query("ALTER TABLE Albumtable DROP Directory", true);
generic_sql_query("UPDATE Statstable SET Value = 2 WHERE Item = 'SchemaVer'", true);
break;
case 2:
logger::log("SQL", "Updating FROM Schema version 2 TO Schema version 3");
generic_sql_query("ALTER TABLE Tracktable ADD Hidden TINYINT(1) UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Tracktable SET Hidden = 0 WHERE Hidden IS NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 3 WHERE Item = 'SchemaVer'", true);
break;
case 3:
logger::log("SQL", "Updating FROM Schema version 3 TO Schema version 4");
generic_sql_query("UPDATE Tracktable SET Disc = 1 WHERE Disc IS NULL OR Disc = 0", true);
generic_sql_query("UPDATE Statstable SET Value = 4 WHERE Item = 'SchemaVer'", true);
break;
case 4:
logger::log("SQL", "Updating FROM Schema version 4 TO Schema version 5");
generic_sql_query("UPDATE Albumtable SET Searched = 0 WHERE Image NOT LIKE 'albumart%'", true);
generic_sql_query("ALTER TABLE Albumtable DROP Image", true);
generic_sql_query("UPDATE Statstable SET Value = 5 WHERE Item = 'SchemaVer'", true);
break;
case 5:
logger::log("SQL", "Updating FROM Schema version 5 TO Schema version 6");
generic_sql_query("DROP INDEX Disc on Tracktable", true);
generic_sql_query("UPDATE Statstable SET Value = 6 WHERE Item = 'SchemaVer'", true);
break;
case 6:
logger::log("SQL", "Updating FROM Schema version 6 TO Schema version 7");
// This was going to be a nice datestamp but newer versions of mysql don't work that way
generic_sql_query("ALTER TABLE Tracktable ADD DateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP", true);
generic_sql_query("UPDATE Tracktable SET DateAdded = FROM_UNIXTIME(LastModified) WHERE LastModified IS NOT NULL AND LastModified > 0", true);
generic_sql_query("UPDATE Statstable SET Value = 7 WHERE Item = 'SchemaVer'", true);
break;
case 7:
logger::log("SQL", "Updating FROM Schema version 7 TO Schema version 8");
// Since we've changed the way we're joining artist names together,
// rather than force the database to be recreated and screw up everyone's
// tags and rating, just modify the artist data.
$stmt = sql_prepare_query_later("UPDATE Artisttable SET Artistname = ? WHERE Artistindex = ?");
if ($stmt !== FALSE) {
$result = generic_sql_query("SELECT * FROM Artisttable", false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
$artist = (string) $obj->Artistname;
$art = explode(' & ', $artist);
if (count($art) > 2) {
$f = array_slice($art, 0, count($art) - 1);
$newname = implode($f, ", ")." & ".$art[count($art) - 1];
logger::log("UPGRADE_SCHEMA", "Updating artist name from ".$artist." to ".$newname);
$stmt->execute(array($newname, $obj->Artistindex));
}
}
}
generic_sql_query("UPDATE Statstable SET Value = 8 WHERE Item = 'SchemaVer'", true);
$stmt = null;
break;
case 8:
logger::log("SQL", "Updating FROM Schema version 8 TO Schema version 9");
// We removed the image column earlier, but I've decided we need it again
// because some mopidy backends supply images and archiving them all makes
// creating the collection take waaaaay too long.
generic_sql_query("ALTER TABLE Albumtable ADD Image VARCHAR(255)", true);
// So we now need to recreate the image database. Sadly this means that people using Beets will lose their album images.
$result = generic_sql_query("SELECT Albumindex, ImgKey FROM Albumtable", false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
if (file_exists('albumart/small/'.$obj->ImgKey.'.jpg')) {
generic_sql_query("UPDATE Albumtable SET Image = 'albumart/small/".$obj->ImgKey.".jpg', Searched = 1 WHERE Albumindex = ".$obj->Albumindex, true);
} else {
generic_sql_query("UPDATE Albumtable SET Image = '', Searched = 0 WHERE Albumindex = ".$obj->Albumindex, true);
}
}
generic_sql_query("UPDATE Statstable SET Value = 9 WHERE Item = 'SchemaVer'", true);
break;
case 9:
logger::log("SQL", "Updating FROM Schema version 9 TO Schema version 10");
generic_sql_query("ALTER TABLE Albumtable DROP NumDiscs", true);
generic_sql_query("UPDATE Statstable SET Value = 10 WHERE Item = 'SchemaVer'", true);
break;
case 10:
logger::log("SQL", "Updating FROM Schema version 10 TO Schema version 11");
generic_sql_query("ALTER TABLE Albumtable DROP IsOneFile", true);
generic_sql_query("UPDATE Statstable SET Value = 11 WHERE Item = 'SchemaVer'", true);
break;
case 11:
logger::log("SQL", "Updating FROM Schema version 11 TO Scheme version 12");
generic_sql_query("ALTER TABLE Tracktable ADD isSearchResult TINYINT(1) UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 12 WHERE Item = 'SchemaVer'", true);
break;
case 12:
logger::log("SQL", "Updating FROM Schema version 12 TO Scheme version 13");
generic_sql_query("ALTER TABLE Albumtable CHANGE Spotilink AlbumUri VARCHAR(255)", true);
generic_sql_query("UPDATE Statstable SET Value = 13 WHERE Item = 'SchemaVer'", true);
break;
case 13:
logger::log("SQL", "Updating FROM Schema version 13 TO Scheme version 14");
// Nothing to do here, this is for SQLite only.
generic_sql_query("UPDATE Statstable SET Value = 14 WHERE Item = 'SchemaVer'", true);
break;
case 14:
logger::log("SQL", "Updating FROM Schema version 14 TO Scheme version 15");
generic_sql_query("ALTER TABLE Tracktable MODIFY LastModified CHAR(32)", true);
generic_sql_query("UPDATE Statstable SET Value = 15 WHERE Item = 'SchemaVer'", true);
break;
case 15:
logger::log("SQL", "Updating FROM Schema version 15 TO Schema version 16");
albumImageBuggery();
generic_sql_query("UPDATE Statstable SET Value = 16 WHERE Item = 'SchemaVer'", true);
break;
case 16:
logger::log("SQL", "Updating FROM Schema version 16 TO Schema version 17");
// Early MPD versions had LastModified as an integer value. They changed it to a datestamp some time ago but I didn't notice
$r = generic_sql_query("SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Tracktable' AND COLUMN_NAME = 'LastModified'");
foreach ($r as $obj) {
logger::log("MYSQL_INIT", "Data Type of LastModified is ".$obj['DATA_TYPE']);
if ($obj['DATA_TYPE'] == 'int') {
logger::log("MYSQL_INIT", "Modifying Tracktable");
generic_sql_query("ALTER TABLE Tracktable ADD LM CHAR(32)", true);
generic_sql_query("UPDATE Tracktable SET LM = LastModified", true);
generic_sql_query("ALTER TABLE Tracktable DROP LastModified", true);
generic_sql_query("ALTER TABLE Tracktable CHANGE LM LastModified CHAR(32)", true);
}
}
generic_sql_query("UPDATE Statstable SET Value = 17 WHERE Item = 'SchemaVer'", true);
break;
case 17:
logger::log("SQL", "Updating FROM Schema version 17 TO Schema version 18");
include("utils/podcastupgrade.php");
generic_sql_query("UPDATE Statstable SET Value = 18 WHERE Item = 'SchemaVer'", true);
break;
case 18:
logger::log("SQL", "Updating FROM Schema version 18 TO Schema version 19");
$result = generic_sql_query('SELECT Tracktable.Uri AS uri, Tracktable.TTindex, Tracktable.Title AS ttit, Albumtable.*, Trackimagetable.Image AS ti FROM Tracktable JOIN Albumtable USING (Albumindex) LEFT JOIN Trackimagetable USING (TTindex) WHERE Tracktable.Uri LIKE "soundcloud:%"', false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
logger::log("SQL", " Creating new Album ".$obj->ttit." Image ".$obj->ti);
$ti = $obj->ti;
if (preg_match('/^http/', $ti)) {
$ti = 'getRemoteImage.php?url='.$ti;
}
if (sql_prepare_query(true, null, null, null,
"INSERT INTO Albumtable
(Albumname, AlbumArtistindex, AlbumUri, Year, Searched, ImgKey, mbid, Domain, Image)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)",
$obj->ttit, $obj->AlbumArtistindex, $obj->uri, $obj->Year, $obj->Searched, $obj->ImgKey, $obj->mbid, $obj->Domain, $ti
)) {
$retval = $mysqlc->lastInsertId();
logger::log("SQL", " .. success, Albumindex ".$retval);
generic_sql_query("UPDATE Tracktable SET Albumindex = ".$retval." WHERE TTindex = ".$obj->TTindex, true);
} else {
logger::log("SQL", " .. ERROR!");
}
}
generic_sql_query("UPDATE Statstable SET Value = 19 WHERE Item = 'SchemaVer'", true);
break;
case 19:
logger::log("SQL", "Updating FROM Schema version 19 TO Schema version 20");
$result = generic_sql_query('SELECT Tracktable.Uri AS uri, Tracktable.TTindex, Tracktable.Title AS ttit, Albumtable.*, Trackimagetable.Image AS ti FROM Tracktable JOIN Albumtable USING (Albumindex) LEFT JOIN Trackimagetable USING (TTindex) WHERE Tracktable.Uri LIKE "youtube:%"', false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
logger::log("SQL", " Creating new Album ".$obj->ttit." Image ".$obj->ti);
$ti = $obj->ti;
if (preg_match('/^http/', $ti)) {
$ti = 'getRemoteImage.php?url='.$ti;
}
if (sql_prepare_query(true, null, null, null,
"INSERT INTO Albumtable
(Albumname, AlbumArtistindex, AlbumUri, Year, Searched, ImgKey, mbid, Domain, Image)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)",
$obj->ttit, $obj->AlbumArtistindex, $obj->uri, $obj->Year, $obj->Searched, $obj->ImgKey, $obj->mbid, $obj->Domain, $ti
)) {
$retval = $mysqlc->lastInsertId();
logger::log("SQL", " .. success, Albumindex ".$retval);
generic_sql_query("UPDATE Tracktable SET Albumindex = ".$retval." WHERE TTindex = ".$obj->TTindex, true);
} else {
logger::error("SQL", " .. ERROR!");
}
}
generic_sql_query("UPDATE Statstable SET Value = 20 WHERE Item = 'SchemaVer'", true);
break;
case 20:
logger::log("SQL", "Updating FROM Schema version 20 TO Schema version 21");
generic_sql_query("DROP TABLE Trackimagetable", true);
generic_sql_query("UPDATE Statstable SET Value = 21 WHERE Item = 'SchemaVer'", true);
break;
case 21:
logger::log("SQL", "Updating FROM Schema version 21 TO Schema version 22");
generic_sql_query("ALTER TABLE Playcounttable ADD LastPlayed TIMESTAMP NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 22 WHERE Item = 'SchemaVer'", true);
break;
case 22:
logger::log("SQL", "Updating FROM Schema version 22 TO Schema version 23");
generic_sql_query("ALTER TABLE Podcasttable ADD Version TINYINT(2)", true);
generic_sql_query("ALTER TABLE PodcastTracktable ADD Guid VARCHAR(2000)", true);
generic_sql_query("ALTER TABLE PodcastTracktable ADD Localfilename VARCHAR(255)", true);
generic_sql_query("UPDATE Podcasttable SET Version = 1 WHERE PODindex IS NOT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 23 WHERE Item = 'SchemaVer'", true);
break;
case 23:
logger::log("SQL", "Updating FROM Schema version 23 TO Schema version 24");
generic_sql_query("ALTER TABLE Tracktable CHANGE DateAdded DateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP", true);
generic_sql_query("UPDATE Statstable SET Value = 24 WHERE Item = 'SchemaVer'", true);
break;
case 24:
logger::log("SQL", "Updating FROM Schema version 24 TO Schema version 25");
generic_sql_query("ALTER DATABASE romprdb CHARACTER SET utf8 COLLATE utf8_unicode_ci", true);
generic_sql_query("UPDATE Statstable SET Value = 25 WHERE Item = 'SchemaVer'", true);
break;
case 25:
logger::log("SQL", "Updating FROM Schema version 25 TO Schema version 26");
generic_sql_query("ALTER TABLE Tracktable ADD justAdded TINYINT(1) UNSIGNED DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 26 WHERE Item = 'SchemaVer'", true);
break;
case 26:
logger::log("SQL", "Updating FROM Schema version 26 TO Schema version 27");
generic_sql_query("ALTER TABLE Albumtable ADD justUpdated TINYINT(1) UNSIGNED DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 27 WHERE Item = 'SchemaVer'", true);
break;
case 27:
logger::log("SQL", "Updating FROM Schema version 27 TO Schema version 28");
rejig_wishlist_tracks();
generic_sql_query("UPDATE Statstable SET Value = 28 WHERE Item = 'SchemaVer'", true);
break;
case 28:
logger::log("SQL", "Updating FROM Schema version 28 TO Schema version 29");
create_update_triggers();
generic_sql_query("UPDATE Statstable SET Value = 29 WHERE Item = 'SchemaVer'", true);
break;
case 29:
logger::log("SQL", "Updating FROM Schema version 29 TO Schema version 30");
include('utils/radioupgrade.php');
generic_sql_query("UPDATE Statstable SET Value = 30 WHERE Item = 'SchemaVer'", true);
break;
case 30:
logger::log("SQL", "Updating FROM Schema version 30 TO Schema version 31");
generic_sql_query("ALTER TABLE PodcastTracktable CHANGE Description Description TEXT", true);
generic_sql_query("ALTER TABLE PodcastTracktable CHANGE Link Link TEXT", true);
generic_sql_query("ALTER TABLE PodcastTracktable CHANGE Guid Guid TEXT", true);
generic_sql_query("ALTER TABLE Podcasttable CHANGE FeedURL FeedURL TEXT", true);
generic_sql_query("ALTER TABLE Podcasttable CHANGE Description Description TEXT", true);
generic_sql_query("ALTER TABLE Tracktable CHANGE Uri Uri TEXT", true);
generic_sql_query("UPDATE Statstable SET Value = 31 WHERE Item = 'SchemaVer'", true);
break;
case 31:
logger::log("SQL", "Updating FROM Schema version 31 TO Schema version 32");
generic_sql_query("ALTER TABLE Podcasttable ADD Subscribed TINYINT(1) NOT NULL DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 32 WHERE Item = 'SchemaVer'", true);
break;
case 32:
logger::log("SQL", "Updating FROM Schema version 32 TO Schema version 33");
generic_sql_query("DROP TRIGGER IF EXISTS track_insert_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS track_update_trigger", true);
create_conditional_triggers();
generic_sql_query("UPDATE Statstable SET Value = 33 WHERE Item = 'SchemaVer'", true);
break;
case 33:
logger::log("SQL", "Updating FROM Schema version 33 TO Schema version 34");
generic_sql_query("ALTER TABLE Albumtable ADD ImgVersion INT UNSIGNED DEFAULT ".ROMPR_IMAGE_VERSION, true);
generic_sql_query("UPDATE Albumtable SET ImgVersion = 1",true);
generic_sql_query("UPDATE Statstable SET Value = 34 WHERE Item = 'SchemaVer'", true);
break;
case 34:
logger::log("SQL", "Updating FROM Schema version 34 TO Schema version 35");
generic_sql_query("ALTER TABLE Tracktable ADD Sourceindex INT UNSIGNED DEFAULT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 35 WHERE Item = 'SchemaVer'", true);
break;
case 35:
generic_sql_query("UPDATE Statstable SET Value = 36 WHERE Item = 'SchemaVer'", true);
break;
case 36:
logger::log("SQL", "Updating FROM Schema version 35 TO Schema version 37");
$localpods = generic_sql_query("SELECT PODTrackindex, PODindex, LocalFilename FROM PodcastTracktable WHERE LocalFilename IS NOT NULL");
foreach ($localpods as $pod) {
sql_prepare_query(true, null, null, null, "UPDATE PodcastTracktable SET LocalFilename = ? WHERE PODTrackindex = ?", '/prefs/podcasts/'.$pod['PODindex'].'/'.$pod['PODTrackindex'].'/'.$pod['LocalFilename'], $pod['PODTrackindex']);
}
generic_sql_query("UPDATE Statstable SET Value = 37 WHERE Item = 'SchemaVer'", true);
break;
case 37:
logger::log("SQL", "Updating FROM Schema version 37 TO Schema version 38");
generic_sql_query("ALTER TABLE Albumtable MODIFY ImgVersion INT UNSIGNED DEFAULT ".ROMPR_IMAGE_VERSION, true);
generic_sql_query("UPDATE Statstable SET Value = 38 WHERE Item = 'SchemaVer'", true);
break;
case 38:
logger::log("SQL", "Updating FROM Schema version 38 TO Schema version 39");
generic_sql_query("ALTER TABLE Podcasttable ADD LastPubDate INT UNSIGNED DEFAULT NULL", true);
generic_sql_query("CREATE INDEX ptt ON PodcastTracktable (Title)", true);
require_once('includes/podcastfunctions.php');
upgrade_podcasts_to_version();
generic_sql_query("UPDATE Statstable SET Value = 39 WHERE Item = 'SchemaVer'", true);
break;
case 39:
logger::log("SQL", "Updating FROM Schema version 39 TO Schema version 40");
// Takes too long. It'll happen when they get refreshed anyway.
// require_once('includes/podcastfunctions.php');
// upgrade_podcast_images();
generic_sql_query("UPDATE Statstable SET Value = 40 WHERE Item = 'SchemaVer'", true);
break;
case 40:
logger::log("SQL", "Updating FROM Schema version 40 TO Schema version 41");
generic_sql_query("ALTER TABLE Podcasttable ADD Category VARCHAR(255) NOT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 41 WHERE Item = 'SchemaVer'", true);
break;
case 41:
logger::log("SQL", "Updating FROM Schema version 41 TO Schema version 42");
generic_sql_query("ALTER TABLE PodcastTracktable ADD Progress INT UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 42 WHERE Item = 'SchemaVer'", true);
break;
case 42:
logger::log("SQL", "Updating FROM Schema version 42 TO Schema version 43");
update_stream_images(43);
generic_sql_query("UPDATE Statstable SET Value = 43 WHERE Item = 'SchemaVer'", true);
break;
case 43:
logger::log("SQL", "Updating FROM Schema version 43 TO Schema version 44");
empty_modified_cache_dirs(44);
generic_sql_query("UPDATE Statstable SET Value = 44 WHERE Item = 'SchemaVer'", true);
break;
case 44:
logger::log("SQL", "Updating FROM Schema version 44 TO Schema version 45");
upgrade_host_defs(45);
generic_sql_query("UPDATE Statstable SET Value = 45 WHERE Item = 'SchemaVer'", true);
break;
case 45:
logger::log("SQL", "Updating FROM Schema version 45 TO Schema version 46");
generic_sql_query("UPDATE Statstable SET Value = 46 WHERE Item = 'SchemaVer'", true);
break;
case 46:
logger::log("SQL", "Updating FROM Schema version 46 TO Schema version 47");
generic_sql_query("ALTER TABLE Playcounttable ADD SyncCount INT UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 47 WHERE Item = 'SchemaVer'", true);
create_playcount_triggers();
break;
case 47:
logger::log("SQL", "Updating FROM Schema version 47 TO Schema version 48");
// Some versions had a default value and an on update for LastPlayed, which is WRONG and fucks things up
generic_sql_query("ALTER TABLE Playcounttable ALTER LastPlayed DROP DEFAULT", true);
generic_sql_query("ALTER TABLE Playcounttable CHANGE LastPlayed LastPlayed TIMESTAMP NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 48 WHERE Item = 'SchemaVer'", true);
break;
case 48:
logger::log("SQL", "Updating FROM Schema version 48 TO Schema version 49");
upgrade_host_defs(49);
generic_sql_query("UPDATE Statstable SET Value = 49 WHERE Item = 'SchemaVer'", true);
break;
case 49:
logger::log("SQL", "Updating FROM Schema version 49 TO Schema version 50");
generic_sql_query("ALTER TABLE Tracktable ADD LinkChecked TINYINT(1) UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 50 WHERE Item = 'SchemaVer'", true);
break;
case 50:
logger::log("SQL", "Updating FROM Schema version 50 TO Schema version 51");
// Something wierd happened and I lost half my triggers. In case it happens to anyone else...
generic_sql_query("DROP TRIGGER IF EXISTS track_insert_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS track_update_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS rating_update_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS rating_insert_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS tag_delete_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS tag_insert_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS tag_remove_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS track_delete_trigger", true);
create_conditional_triggers();
create_update_triggers();
generic_sql_query("UPDATE Statstable SET Value = 51 WHERE Item = 'SchemaVer'", true);
break;
case 51:
logger::log("SQL", "Updating FROM Schema version 51 TO Schema version 52");
generic_sql_query("ALTER TABLE Tracktable ADD isAudiobook TINYINT(1) UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 52 WHERE Item = 'SchemaVer'", true);
break;
case 52:
logger::log("SQL", "Updating FROM Schema version 52 TO Schema version 53");
create_progress_triggers();
generic_sql_query("UPDATE Statstable SET Value = 53 WHERE Item = 'SchemaVer'", true);
break;
case 53:
logger::log("SQL", "Updating FROM Schema version 53 TO Schema version 54");
require_once ('utils/backgroundimages.php');
first_upgrade_of_user_backgrounds();
generic_sql_query("UPDATE Statstable SET Value = 54 WHERE Item = 'SchemaVer'", true);
break;
case 54:
logger::log("SQL", "Updating FROM Schema version 54 TO Schema version 55");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('PodUpPid', 0)", true);
generic_sql_query("UPDATE Statstable SET Value = 55 WHERE Item = 'SchemaVer'", true);
break;
case 55:
logger::log("SQL", "Updating FROM Schema version 55 TO Schema version 56");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('Updating', '0')", true);
generic_sql_query("UPDATE Statstable SET Value = 56 WHERE Item = 'SchemaVer'", true);
break;
}
$sv++;
}
return array(true, "");
}
function delete_oldtracks() {
// generic_sql_query("DELETE Tracktable FROM Tracktable JOIN Playcounttable USING (TTindex) WHERE Hidden = 1 AND DATE_SUB(CURDATE(), INTERVAL 6 MONTH) > DateAdded AND Playcount < 2", true);
}
function delete_orphaned_artists() {
generic_sql_query("DROP TABLE IF EXISTS Croft", true);
generic_sql_query("DROP TABLE IF EXISTS Cruft", true);
generic_sql_query("CREATE TEMPORARY TABLE Croft(Artistindex INT UNSIGNED NOT NULL UNIQUE, PRIMARY KEY(Artistindex)) AS SELECT Artistindex FROM Tracktable UNION SELECT AlbumArtistindex FROM Albumtable", true);
generic_sql_query("CREATE TEMPORARY TABLE Cruft(Artistindex INT UNSIGNED NOT NULL UNIQUE, PRIMARY KEY(Artistindex)) AS SELECT Artistindex FROM Artisttable WHERE Artistindex NOT IN (SELECT Artistindex FROM Croft)", true);
generic_sql_query("DELETE Artisttable FROM Artisttable INNER JOIN Cruft ON Artisttable.Artistindex = Cruft.Artistindex", true);
}
function hide_played_tracks() {
generic_sql_query("CREATE TEMPORARY TABLE Fluff(TTindex INT UNSIGNED NOT NULL UNIQUE, PRIMARY KEY(TTindex)) AS SELECT TTindex FROM Tracktable JOIN Playcounttable USING (TTindex) WHERE isSearchResult = 2", true);
generic_sql_query("UPDATE Tracktable SET Hidden = 1, isSearchResult = 0 WHERE TTindex IN (SELECT TTindex FROM Fluff)", true);
}
function sql_recent_tracks() {
global $prefs;
$qstring = "SELECT TTindex FROM Tracktable WHERE (DATE_SUB(CURDATE(),INTERVAL 60 DAY) <= DateAdded) AND Hidden = 0 AND isSearchResult < 2 AND isAudiobook = 0 AND Uri IS NOT NULL";
if ($prefs['collection_player'] == 'mopidy' && $prefs['player_backend'] == 'mpd') {
$qstring .= ' AND Uri LIKE "local:%"';
}
return $qstring." ORDER BY RAND()";
}
function sql_recent_albums() {
global $prefs;
$qstring = "SELECT TTindex, Albumindex, TrackNo FROM Tracktable WHERE DATE_SUB(CURDATE(),INTERVAL 60 DAY) <= DateAdded AND Hidden = 0 AND isSearchResult < 2 AND isAudiobook = 0 AND Uri IS NOT NULL";
if ($prefs['collection_player'] == 'mopidy' && $prefs['player_backend'] == 'mpd') {
$qstring .= ' AND Uri LIKE "local:%"';
}
return $qstring;
}
function sql_recently_played() {
return "SELECT t.Uri, t.Title, a.Artistname, al.Albumname, al.Image, al.ImgKey, UNIX_TIMESTAMP(p.LastPlayed) AS unixtime FROM Tracktable AS t JOIN Playcounttable AS p USING (TTindex) JOIN Albumtable AS al USING (albumindex) JOIN Artisttable AS a ON (a.Artistindex = al.AlbumArtistindex) WHERE DATE_SUB(CURDATE(),INTERVAL 14 DAY) <= p.LastPlayed AND p.LastPlayed IS NOT NULL ORDER BY p.LastPlayed DESC";
}
function recently_played_playlist() {
$qstring = "SELECT TTindex FROM Playcounttable JOIN Tracktable USING (TTindex) WHERE DATE_SUB(CURDATE(),INTERVAL 14 DAY) <= LastPlayed AND LastPlayed IS NOT NULL AND isAudiobook = 0 AND Hidden = 0";
return $qstring;
}
function sql_two_weeks() {
return "DATE_SUB(CURDATE(),INTERVAL 14 DAY) > LastPlayed";
}
function sql_two_weeks_include($days) {
return "DATE_SUB(CURDATE(),INTERVAL ".$days." DAY) <= LastPlayed AND LastPlayed IS NOT NULL";
}
function sql_to_unixtime($s) {
return "UNIX_TIMESTAMP(".$s.")";
}
function track_date_check($range, $flag) {
if ($flag == 'b') {
return '';
}
switch ($range) {
case ADDED_ALL_TIME:
return '';
break;
case ADDED_TODAY:
return 'AND DATE_SUB(CURDATE(), INTERVAL 1 DAY) <= DateAdded';
break;
case ADDED_THIS_WEEK:
return 'AND DATE_SUB(CURDATE(), INTERVAL 7 DAY) <= DateAdded';
break;
case ADDED_THIS_MONTH:
return 'AND DATE_SUB(CURDATE(), INTERVAL 1 MONTH) <= DateAdded';
break;
case ADDED_THIS_YEAR:
return 'AND DATE_SUB(CURDATE(), INTERVAL 1 YEAR) <= DateAdded';
break;
default:
logger::warn("SQL", "ERROR! Unknown Collection Range ".$range);
return '';
break;
}
}
function create_conditional_triggers() {
generic_sql_query("CREATE TRIGGER track_insert_trigger AFTER INSERT ON Tracktable
FOR EACH ROW
BEGIN
IF (NEW.Hidden=0)
THEN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = NEW.Albumindex;
END IF;
END;", true);
generic_sql_query("CREATE TRIGGER track_update_trigger AFTER UPDATE ON Tracktable
FOR EACH ROW
BEGIN
IF (NEW.Hidden=0)
THEN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = NEW.Albumindex;
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = OLD.Albumindex;
END IF;
END;", true);
}
function create_playcount_triggers() {
generic_sql_query("CREATE TRIGGER syncupdatetrigger BEFORE UPDATE ON Playcounttable
FOR EACH ROW
BEGIN
IF (NEW.Playcount > OLD.Playcount)
THEN
SET NEW.SyncCount = OLD.SyncCount + 1;
END IF;
END;", true);
generic_sql_query("CREATE TRIGGER syncinserttrigger BEFORE INSERT ON Playcounttable
FOR EACH ROW
BEGIN
SET NEW.SyncCount = 1;
END;", true);
}
function create_update_triggers() {
logger::debug("MYSQL", "Creating Triggers for update operation");
generic_sql_query("CREATE TRIGGER rating_update_trigger AFTER UPDATE ON Ratingtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER rating_insert_trigger AFTER INSERT ON Ratingtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_delete_trigger AFTER DELETE ON Tagtable
FOR EACH ROW
BEGIN
DELETE FROM TagListtable WHERE Tagindex = OLD.Tagindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_insert_trigger AFTER INSERT ON TagListtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_remove_trigger AFTER DELETE ON TagListtable
FOR EACH ROW
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = OLD.TTindex);", true);
generic_sql_query("CREATE TRIGGER track_delete_trigger AFTER DELETE ON Tracktable
FOR EACH ROW
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = OLD.Albumindex;", true);
}
function create_progress_triggers() {
generic_sql_query("CREATE TRIGGER progress_update_trigger AFTER UPDATE ON Progresstable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
END;", true);
generic_sql_query("CREATE TRIGGER progress_insert_trigger AFTER INSERT ON Progresstable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
END;", true);
}
?>

View File

@ -0,0 +1,29 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("backends/sql/backend.php");
print '<table align="center" style="border-collapse:collapse;width:90%">';
$date = '';
$result = generic_sql_query(sql_recently_played(), false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
$thisdate = date('l, jS F Y', $obj->unixtime);
if ($thisdate != $date) {
$date = $thisdate;
print '<tr class="tagh datetag"><th colspan="3">'.$date.'</th></tr>';
}
print '<tr class="draggable clicktrack playable spacerboogie" name="'.rawurlencode($obj->Uri).'">';
print '<td width="40px"><img class="smallcover';
if ($obj->Image) {
print '" src="'.$obj->Image;
} else {
print ' notfound';
}
print '" /></td>';
print '<td class="dan"><b>'.$obj->Title.'</b><br><i>by</i> <b>'.$obj->Artistname.'</b><br><i>on</i> <b>'.$obj->Albumname.'</b></td>';
print '<td class="dan">'.date('H:i', $obj->unixtime).'</td>';
print '</tr>';
}
print '</table>';
?>

View File

@ -0,0 +1,15 @@
# These are the settings that I found made a MASSIVE difference to
# performance. Make them as high as you dare, except for
# innodb_flush_log_at_trx_commit, which should be 0.
# NOTE that the last setting can cause you to lose one second's worth
# of data on a crash. If this is important, then don't enable this setting
# put these in /etc/mysql/conf.d/rompr-tweaks.cnf
[mysqld]
query_cache_limit = 16M
query_cache_size = 64M
binlog_ignore_db = romprdb
innodb_buffer_pool_size = 64M
innodb_flush_log_at_trx_commit = 0

View File

@ -0,0 +1,984 @@
<?php
define('SQL_RANDOM_SORT', 'RANDOM()');
define('SQL_TAG_CONCAT', "GROUP_CONCAT(t.Name,', ') ");
function connect_to_database($sp = true) {
global $mysqlc, $prefs;
if ($mysqlc !== null) {
logger::error("SQLITE", "AWOOOGA! ATTEMPTING MULTIPLE DATABASE CONNECTIONS!");
return;
}
try {
$dsn = "sqlite:prefs/collection.sq3";
logger::trace('SQLITE','Opening collection',$dsn);
$mysqlc = new PDO($dsn);
logger::debug("MYSQL", "Connected to SQLite");
// This increases performance
generic_sql_query('PRAGMA journal_mode=DELETE', true);
generic_sql_query('PRAGMA cache_size=-4000', true);
generic_sql_query('PRAGMA synchronous=OFF', true);
generic_sql_query('PRAGMA threads=4', true);
readCollectionPlayer($sp);
} catch (Exception $e) {
logger::error("MYSQL", "Couldn't Connect To SQLite - ".$e);
sql_init_fail($e->getMessage());
}
}
function close_database() {
global $mysqlc;
$mysqlc = null;
}
function check_sql_tables() {
global $mysqlc, $prefs;
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Tracktable(".
"TTindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Title VARCHAR(255), ".
"Albumindex INTEGER, ".
"TrackNo SMALLINT, ".
"Duration INTEGER, ".
"Artistindex INTEGER, ".
"Disc TINYINT(3), ".
"Uri TEXT,".
"LastModified CHAR(32), ".
"Hidden TINYINT(1) DEFAULT 0, ".
"DateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ".
"isSearchResult TINYINT(1) DEFAULT 0, ".
"Sourceindex INTEGER DEFAULT NULL, ".
"LinkChecked TINYINT(1) DEFAULT 0, ".
"isAudiobook TINYINT(1) DEFAULT 0, ".
"justAdded TINYINT(1) DEFAULT 1)", true))
{
logger::log("SQLITE_CONNECT", " Tracktable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ai ON Tracktable (Albumindex)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ti ON Tracktable (Title)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS tn ON Tracktable (TrackNo)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS di ON Tracktable (Disc)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Albumtable(".
"Albumindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Albumname VARCHAR(255), ".
"AlbumArtistindex INTEGER, ".
"AlbumUri VARCHAR(255), ".
"Year YEAR, ".
"Searched TINYINT(1), ".
"ImgKey CHAR(32), ".
"mbid CHAR(40), ".
"ImgVersion INTEGER DEFAULT ".ROMPR_IMAGE_VERSION.", ".
"Domain CHAR(32), ".
"Image VARCHAR(255), ".
"justUpdated TINYINT(1) DEFAULT 0)", true))
{
logger::log("SQLITE_CONNECT", " Albumtable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ni ON Albumtable (Albumname)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS aai ON Albumtable (AlbumArtistindex)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS di ON Albumtable (Domain)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ii ON Albumtable (ImgKey)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Albumtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Artisttable(".
"Artistindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Artistname VARCHAR(255))", true))
{
logger::log("SQLITE_CONNECT", " Artisttable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ni ON Artisttable (Artistname)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Artisttable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Artisttable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Ratingtable(".
"TTindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Rating TINYINT(1))", true))
{
logger::log("SQLITE_CONNECT", " Ratingtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Ratingtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Progresstable(".
"TTindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Progress INTEGER)", true))
{
logger::log("SQLITE_CONNECT", " Progresstable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Progresstable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Tagtable(".
"Tagindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Name VARCHAR(255))", true))
{
logger::log("SQLITE_CONNECT", " Tagtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Tagtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS TagListtable(".
"Tagindex INTEGER NOT NULL REFERENCES Tagtable(Tagindex), ".
"TTindex INTEGER NOT NULL REFERENCES Tracktable(TTindex), ".
"PRIMARY KEY (Tagindex, TTindex))", true))
{
logger::log("SQLITE_CONNECT", " TagListtable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking TagListtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Playcounttable(".
"TTindex INTEGER PRIMARY KEY NOT NULL UNIQUE REFERENCES Tracktable(TTindex), ".
"Playcount INT UNSIGNED NOT NULL, ".
"SyncCount INT UNSIGNED DEFAULT 0, ".
"LastPlayed TIMESTAMP DEFAULT NULL)", true))
{
logger::log("SQLITE_CONNECT", " Playcounttable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Playcounttable : ".$err);
}
if (!generic_sql_query("CREATE TABLE IF NOT EXISTS Statstable(Item CHAR(11), Value INTEGER, PRIMARY KEY(Item))", true)) {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Statstable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS Podcasttable(".
"PODindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"FeedURL TEXT, ".
"LastUpdate INTEGER, ".
"Image VARCHAR(255), ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"RefreshOption TINYINT(2) DEFAULT 0, ".
"SortMode TINYINT(2) DEFAULT 0, ".
"HideDescriptions TINYINT(1) DEFAULT 0, ".
"DisplayMode TINYINT(2) DEFAULT 0, ".
"DaysToKeep INTEGER DEFAULT 0, ".
"NumToKeep INTEGER DEFAULT 0, ".
"KeepDownloaded TINYINT(1) DEFAULT 0, ".
"AutoDownload TINYINT(1) DEFAULT 0, ".
"DaysLive INTEGER, ".
"Version TINYINT(2), ".
"Subscribed TINYINT(1) NOT NULL DEFAULT 1, ".
"Description TEXT, ".
"LastPubDate INTEGER DEFAULT NULL, ".
"Category VARCHAR(255))", true))
{
logger::log("SQLITE_CONNECT", " Podcasttable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking Podcasttable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS PodcastTracktable(".
"PODTrackindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"JustUpdated TINYINT(1), ".
"PODindex INTEGER, ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"Duration INTEGER, ".
"PubDate INTEGER, ".
"FileSize INTEGER, ".
"Description TEXT, ".
"Link TEXT, ".
"Guid TEXT, ".
"Localfilename VARCHAR(255), ".
"Downloaded TINYINT(1) DEFAULT 0, ".
"Listened TINYINT(1) DEFAULT 0, ".
"New TINYINT(1) DEFAULT 1, ".
"Progress INTEGER DEFAULT 0, ".
"Deleted TINYINT(1) DEFAULT 0)", true))
{
logger::log("SQLITE_CONNECT", " PodcastTracktable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ptt ON PodcastTracktable (Title)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking PodcastTracktable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking PodcastTracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS RadioStationtable(".
"Stationindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Number SMALLINT DEFAULT 65535, ".
"IsFave TINYINT(1), ".
"StationName VARCHAR(255), ".
"PlaylistUrl TEXT, ".
"Image VARCHAR(255))", true))
{
logger::log("SQLITE_CONNECT", " RadioStationtable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS ui ON RadioStationtable (PlaylistUrl)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioStationtable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioStationtable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS RadioTracktable(".
"Trackindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Stationindex INTEGER REFERENCES RadioStationtable(Stationindex), ".
"TrackUri TEXT, ".
"PrettyStream TEXT)", true))
{
logger::log("SQLITE_CONNECT", " RadioTracktable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS uri ON RadioTracktable (TrackUri)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioTracktable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking RadioTracktable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS WishlistSourcetable(".
"Sourceindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"SourceName VARCHAR(255), ".
"SourceImage VARCHAR(255), ".
"SourceUri TEXT)", true))
{
logger::log("SQLITE_CONNECT", " WishlistSourcetable OK");
if (generic_sql_query("CREATE INDEX IF NOT EXISTS suri ON WishlistSourcetable (SourceUri)", true)) {
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking WishlistSourcetable : ".$err);
}
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking WishlistSourcetable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS AlbumsToListenTotable(".
"Listenindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"JsonData TEXT)", true))
{
logger::log("MYSQL_CONNECT", " AlbumsToListenTotabletable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking AlbumsToListenTotable : ".$err);
}
if (generic_sql_query("CREATE TABLE IF NOT EXISTS BackgroundImageTable(".
"BgImageIndex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Skin VARCHAR(255), ".
"BrowserID VARCHAR(20) DEFAULT NULL, ".
"Filename VARCHAR(255), ".
"Orientation TINYINT(2))", true))
{
logger::log("MYSQL_CONNECT", " BackgounrdImageTable OK");
} else {
$err = $mysqlc->errorInfo()[2];
return array(false, "Error While Checking BackgroundImageTable : ".$err);
}
// Check schema version and update tables as necessary
$sv = simple_query('Value', 'Statstable', 'Item', 'SchemaVer', 0);
if ($sv == 0) {
logger::log("SQLITE_CONNECT", "No Schema Version Found - initialising table");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('ListVersion', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('ArtistCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('AlbumCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('TrackCount', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('TotalTime', '0')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('CollType', '999')", true);
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('SchemaVer', '".ROMPR_SCHEMA_VERSION."')", true);
$sv = ROMPR_SCHEMA_VERSION;
logger::log("SQLITE_CONNECT", "Statstable populated");
create_update_triggers();
create_conditional_triggers();
create_playcount_triggers();
}
if ($sv > ROMPR_SCHEMA_VERSION) {
logger::log("SQLITE_CONNECT", "Schema Mismatch! We are version ".ROMPR_SCHEMA_VERSION." but database is version ".$sv);
return array(false, "Your database has version number ".$sv." but this version of rompr only handles version ".ROMPR_SCHEMA_VERSION);
}
while ($sv < ROMPR_SCHEMA_VERSION) {
switch ($sv) {
case 0:
logger::log("SQL", "BIG ERROR! No Schema Version found!!");
return array(false, "Database Error - could not read schema version. Cannot continue.");
break;
case 11:
logger::log("SQL", "Updating FROM Schema version 11 TO Scheme version 12");
generic_sql_query("ALTER TABLE Tracktable ADD isSearchResult TINYINT(1) DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 12 WHERE Item = 'SchemaVer'", true);
break;
case 12;
logger::log("SQL", "Updating FROM Schema version 12 TO Scheme version 13");
// First attempt didn't work
generic_sql_query("UPDATE Statstable SET Value = 13 WHERE Item = 'SchemaVer'", true);
break;
case 13:
// SQLite doesn't let you rename or remove a column. Holy Shitting heck.
logger::log("SQL", "Updating FROM Schema version 13 TO Schema version 14");
generic_sql_query("CREATE TABLE Albumtable_New(".
"Albumindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Albumname VARCHAR(255), ".
"AlbumArtistindex INTEGER, ".
"AlbumUri VARCHAR(255), ".
"Year YEAR, ".
"Searched TINYINT(1), ".
"ImgKey CHAR(32), ".
"mbid CHAR(40), ".
"Domain CHAR(32), ".
"Image VARCHAR(255))", true);
generic_sql_query("INSERT INTO Albumtable_New SELECT Albumindex, Albumname,
AlbumArtistindex, Spotilink AS AlbumUri, Year, Searched, ImgKey, mbid, Domain, Image
FROM Albumtable", true);
generic_sql_query("DROP TABLE Albumtable", true);
generic_sql_query("ALTER TABLE Albumtable_New RENAME TO Albumtable", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ni ON Albumtable (Albumname)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS aai ON Albumtable (AlbumArtistindex)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS di ON Albumtable (Domain)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ii ON Albumtable (ImgKey)", true);
generic_sql_query("UPDATE Statstable SET Value = 14 WHERE Item = 'SchemaVer'", true);
break;
case 14:
// SQLite doesn't let you rename or remove a column. Holy Shitting heck.
logger::log("SQL", "Updating FROM Schema version 14 TO Schema version 15");
generic_sql_query("CREATE TABLE Tracktable_New(".
"TTindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Title VARCHAR(255), ".
"Albumindex INTEGER, ".
"TrackNo SMALLINT, ".
"Duration INTEGER, ".
"Artistindex INTEGER, ".
"Disc TINYINT(3), ".
"Uri VARCHAR(2000) ,".
"LastModified CHAR(32), ".
"Hidden TINYINT(1) DEFAULT 0, ".
"DateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ".
"isSearchResult TINYINT(1) DEFAULT 0)", true);
generic_sql_query("INSERT INTO Tracktable_New SELECT TTindex, Title, Albumindex,
TrackNo, Duration, Artistindex, Disc, Uri, LastModified, Hidden, DateAdded, isSearchResult
FROM Tracktable", true);
generic_sql_query("DROP TABLE Tracktable", true);
generic_sql_query("ALTER TABLE Tracktable_New RENAME TO Tracktable", true);
generic_sql_query("CREATE TRIGGER IF NOT EXISTS updatetime AFTER UPDATE ON Tracktable BEGIN UPDATE Tracktable SET DateAdded = CURRENT_TIMESTAMP WHERE TTindex = old.TTindex; END", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ai ON Tracktable (Albumindex)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ti ON Tracktable (Title)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS tn ON Tracktable (TrackNo)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS di ON Tracktable (Disc)", true);
generic_sql_query("UPDATE Statstable SET Value = 15 WHERE Item = 'SchemaVer'", true);
break;
case 15:
logger::log("SQL", "Updating FROM Schema version 15 TO Schema version 16");
albumImageBuggery();
generic_sql_query("UPDATE Statstable SET Value = 16 WHERE Item = 'SchemaVer'", true);
break;
case 16:
//Nothing to do here
logger::log("SQL", "Updating FROM Schema version 16 TO Schema version 17");
generic_sql_query("UPDATE Statstable SET Value = 17 WHERE Item = 'SchemaVer'", true);
break;
case 17:
logger::log("SQL", "Updating FROM Schema version 17 TO Schema version 18");
include("utils/podcastupgrade.php");
generic_sql_query("UPDATE Statstable SET Value = 18 WHERE Item = 'SchemaVer'", true);
break;
case 18:
logger::log("SQL", "Updating FROM Schema version 18 TO Schema version 19");
$result = generic_sql_query('SELECT Tracktable.Uri AS uri, Tracktable.TTindex, Tracktable.Title AS ttit, Albumtable.*, Trackimagetable.Image AS ti FROM Tracktable JOIN Albumtable USING (Albumindex) LEFT JOIN Trackimagetable USING (TTindex) WHERE Tracktable.Uri LIKE "soundcloud:%"', false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
logger::log("SQL", " Creating new Album ".$obj->ttit." Image ".$obj->ti);
$ti = $obj->ti;
if (preg_match('/^http/', $ti)) {
$ti = 'getRemoteImage.php?url='.$ti;
}
if (sql_prepare_query(true, null, null, null,
"INSERT INTO Albumtable
(Albumname, AlbumArtistindex, AlbumUri, Year, Searched, ImgKey, mbid, Domain, Image)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)",
$obj->ttit, $obj->AlbumArtistindex, $obj->uri, $obj->Year, $obj->Searched, $obj->ImgKey, $obj->mbid, $obj->Domain, $ti
)) {
$retval = $mysqlc->lastInsertId();
logger::log("SQL", " .. success, Albumindex ".$retval);
generic_sql_query("UPDATE Tracktable SET Albumindex = ".$retval." WHERE TTindex = ".$obj->TTindex, true);
} else {
logger::log("SQL", " .. ERROR!");
}
}
generic_sql_query("UPDATE Statstable SET Value = 19 WHERE Item = 'SchemaVer'");
break;
case 19:
logger::log("SQL", "Updating FROM Schema version 19 TO Schema version 20");
$result = generic_sql_query('SELECT Tracktable.Uri AS uri, Tracktable.TTindex, Tracktable.Title AS ttit, Albumtable.*, Trackimagetable.Image AS ti FROM Tracktable JOIN Albumtable USING (Albumindex) LEFT JOIN Trackimagetable USING (TTindex) WHERE Tracktable.Uri LIKE "youtube:%"', false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
logger::log("SQL", " Creating new Album ".$obj->ttit." Image ".$obj->ti);
$ti = $obj->ti;
if (preg_match('/^http/', $ti)) {
$ti = 'getRemoteImage.php?url='.$ti;
}
if (sql_prepare_query(true, null, null, null,
"INSERT INTO Albumtable
(Albumname, AlbumArtistindex, AlbumUri, Year, Searched, ImgKey, mbid, Domain, Image)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)",
$obj->ttit, $obj->AlbumArtistindex, $obj->uri, $obj->Year, $obj->Searched, $obj->ImgKey, $obj->mbid, $obj->Domain, $ti
)) {
$retval = $mysqlc->lastInsertId();
logger::log("SQL", " .. success, Albumindex ".$retval);
generic_sql_query("UPDATE Tracktable SET Albumindex = ".$retval." WHERE TTindex = ".$obj->TTindex, true);
} else {
logger::log("SQL", " .. ERROR!");
}
}
generic_sql_query("UPDATE Statstable SET Value = 20 WHERE Item = 'SchemaVer'", true);
break;
case 20:
logger::log("SQL", "Updating FROM Schema version 20 TO Schema version 21");
generic_sql_query("DROP TABLE Trackimagetable", true);
generic_sql_query("UPDATE Statstable SET Value = 21 WHERE Item = 'SchemaVer'", true);
break;
case 21:
logger::log("SQL", "Updating FROM Schema version 21 TO Schema version 22");
generic_sql_query("ALTER TABLE Playcounttable ADD COLUMN LastPlayed TIMESTAMP DEFAULT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 22 WHERE Item = 'SchemaVer'", true);
break;
case 22:
logger::log("SQL", "Updating FROM Schema version 22 TO Schema version 23");
generic_sql_query("ALTER TABLE Podcasttable ADD COLUMN Version TINYINT(2)", true);
generic_sql_query("ALTER TABLE PodcastTracktable ADD COLUMN Guid VARCHAR(2000)", true);
generic_sql_query("ALTER TABLE PodcastTracktable ADD COLUMN Localfilename VARCHAR(255)", true);
generic_sql_query("UPDATE Podcasttable SET Version = 1 WHERE PODindex IS NOT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 23 WHERE Item = 'SchemaVer'", true);
break;
case 23:
logger::log("SQL", "Updating FROM Schema version 23 TO Schema version 24");
generic_sql_query("DROP TRIGGER IF EXISTS updatetime", true);
generic_sql_query("UPDATE Statstable SET Value = 24 WHERE Item = 'SchemaVer'", true);
break;
case 24:
logger::log("SQL", "Updating FROM Schema version 24 TO Schema version 25");
// Nothing to do here
generic_sql_query("UPDATE Statstable SET Value = 25 WHERE Item = 'SchemaVer'", true);
break;
case 25:
logger::log("SQL", "Updating FROM Schema version 25 TO Schema version 26");
generic_sql_query("ALTER TABLE Tracktable ADD justAdded TINYINT(1) DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 26 WHERE Item = 'SchemaVer'", true);
break;
case 26:
logger::log("SQL", "Updating FROM Schema version 26 TO Schema version 27");
generic_sql_query("ALTER TABLE Albumtable ADD justUpdated TINYINT(1) DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 27 WHERE Item = 'SchemaVer'", true);
break;
case 27:
logger::log("SQL", "Updating FROM Schema version 27 TO Schema version 28");
rejig_wishlist_tracks();
generic_sql_query("UPDATE Statstable SET Value = 28 WHERE Item = 'SchemaVer'", true);
break;
case 28:
logger::log("SQL", "Updating FROM Schema version 28 TO Schema version 29");
create_update_triggers();
generic_sql_query("UPDATE Statstable SET Value = 29 WHERE Item = 'SchemaVer'", true);
break;
case 29:
logger::log("SQL", "Updating FROM Schema version 29 TO Schema version 30");
include('utils/radioupgrade.php');
generic_sql_query("UPDATE Statstable SET Value = 30 WHERE Item = 'SchemaVer'", true);
break;
case 30:
logger::log("SQL", "Updating FROM Schema version 30 TO Schema version 31");
// No need to do anything here
generic_sql_query("UPDATE Statstable SET Value = 31 WHERE Item = 'SchemaVer'", true);
break;
case 31:
logger::log("SQL", "Updating FROM Schema version 31 TO Schema version 32");
generic_sql_query("ALTER TABLE Podcasttable ADD Subscribed TINYINT(1) NOT NULL DEFAULT 1", true);
generic_sql_query("UPDATE Statstable SET Value = 32 WHERE Item = 'SchemaVer'", true);
break;
case 32:
logger::log("SQL", "Updating FROM Schema version 32 TO Schema version 33");
generic_sql_query("DROP TRIGGER IF EXISTS track_insert_trigger", true);
generic_sql_query("DROP TRIGGER IF EXISTS track_update_trigger", true);
create_conditional_triggers();
generic_sql_query("UPDATE Statstable SET Value = 33 WHERE Item = 'SchemaVer'", true);
break;
case 33:
logger::log("SQL", "Updating FROM Schema version 33 TO Schema version 34");
generic_sql_query("ALTER TABLE Albumtable ADD COLUMN ImgVersion INTEGER DEFAULT ".ROMPR_IMAGE_VERSION, true);
generic_sql_query("UPDATE Albumtable SET ImgVersion = 1",true);
generic_sql_query("UPDATE Statstable SET Value = 34 WHERE Item = 'SchemaVer'", true);
break;
case 34:
logger::log("SQL", "Updating FROM Schema version 34 TO Schema version 35");
generic_sql_query("ALTER TABLE Tracktable ADD COLUMN Sourceindex INTEGER DEFAULT NULL", true);
generic_sql_query("UPDATE Statstable SET Value = 35 WHERE Item = 'SchemaVer'", true);
break;
case 35:
generic_sql_query("UPDATE Statstable SET Value = 36 WHERE Item = 'SchemaVer'", true);
break;
case 36:
logger::log("SQL", "Updating FROM Schema version 35 TO Schema version 37");
$localpods = generic_sql_query("SELECT PODTrackindex, PODindex, LocalFilename FROM PodcastTracktable WHERE LocalFilename IS NOT NULL");
foreach ($localpods as $pod) {
sql_prepare_query(true, null, null, null, "UPDATE PodcastTracktable SET LocalFilename = ? WHERE PODTrackindex = ?", '/prefs/podcasts/'.$pod['PODindex'].'/'.$pod['PODTrackindex'].'/'.$pod['LocalFilename'], $pod['PODTrackindex']);
}
generic_sql_query("UPDATE Statstable SET Value = 37 WHERE Item = 'SchemaVer'", true);
break;
case 37:
logger::log("SQL", "Updating FROM Schema version 37 TO Schema version 38");
generic_sql_query("CREATE TABLE IF NOT EXISTS Albumtable_New(".
"Albumindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"Albumname VARCHAR(255), ".
"AlbumArtistindex INTEGER, ".
"AlbumUri VARCHAR(255), ".
"Year YEAR, ".
"Searched TINYINT(1), ".
"ImgKey CHAR(32), ".
"mbid CHAR(40), ".
"ImgVersion INTEGER DEFAULT ".ROMPR_IMAGE_VERSION.", ".
"Domain CHAR(32), ".
"Image VARCHAR(255), ".
"justUpdated TINYINT(1) DEFAULT 0)", true);
generic_sql_query("INSERT INTO Albumtable_New SELECT Albumindex, Albumname, AlbumArtistindex,
AlbumUri, Year, Searched, ImgKey, mbid, ImgVersion, Domain, Image, justUpdated
FROM Albumtable", true);
generic_sql_query("DROP TABLE Albumtable", true);
generic_sql_query("ALTER TABLE Albumtable_New RENAME TO Albumtable", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ni ON Albumtable (Albumname)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS aai ON Albumtable (AlbumArtistindex)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS di ON Albumtable (Domain)", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ii ON Albumtable (ImgKey)", true);
generic_sql_query("UPDATE Statstable SET Value = 38 WHERE Item = 'SchemaVer'", true);
break;
case 38:
logger::log("SQL", "Updating FROM Schema version 38 TO Schema version 39");
generic_sql_query("ALTER TABLE Podcasttable ADD LastPubDate INTEGER DEFAULT NULL", true);
require_once('includes/podcastfunctions.php');
upgrade_podcasts_to_version();
generic_sql_query("UPDATE Statstable SET Value = 39 WHERE Item = 'SchemaVer'", true);
break;
case 39:
logger::log("SQL", "Updating FROM Schema version 39 TO Schema version 40");
// Takes too long. It'll happen when they get refreshed anyway.
// require_once('includes/podcastfunctions.php');
// upgrade_podcast_images();
generic_sql_query("UPDATE Statstable SET Value = 40 WHERE Item = 'SchemaVer'", true);
break;
case 40:
logger::log("SQL", "Updating FROM Schema version 40 TO Schema version 41");
generic_sql_query("CREATE TABLE IF NOT EXISTS Podcasttable_New(".
"PODindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"FeedURL TEXT, ".
"LastUpdate INTEGER, ".
"Image VARCHAR(255), ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"RefreshOption TINYINT(2) DEFAULT 0, ".
"SortMode TINYINT(2) DEFAULT 0, ".
"HideDescriptions TINYINT(1) DEFAULT 0, ".
"DisplayMode TINYINT(2) DEFAULT 0, ".
"DaysToKeep INTEGER DEFAULT 0, ".
"NumToKeep INTEGER DEFAULT 0, ".
"KeepDownloaded TINYINT(1) DEFAULT 0, ".
"AutoDownload TINYINT(1) DEFAULT 0, ".
"DaysLive INTEGER, ".
"Version TINYINT(2), ".
"Subscribed TINYINT(1) NOT NULL DEFAULT 1, ".
"Description TEXT, ".
"LastPubDate INTEGER DEFAULT NULL, ".
"Category VARCHAR(255))", true);
generic_sql_query("INSERT INTO Podcasttable_New SELECT PODindex, FeedURL, LastUpdate, Image, Title, Artist,
RefreshOption, SortMode, HideDescriptions, DisplayMode, DaysToKeep, NumToKeep, KeepDownloaded, AutoDownload,
DaysLive, Version, Subscribed, Description, LastPubDate, '' AS Category
FROM Podcasttable", true);
generic_sql_query("DROP TABLE Podcasttable", true);
generic_sql_query("ALTER TABLE Podcasttable_New RENAME TO Podcasttable", true);
generic_sql_query("UPDATE Statstable SET Value = 41 WHERE Item = 'SchemaVer'", true);
break;
case 41:
logger::log("SQL", "Updating FROM Schema version 41 TO Schema version 42");
generic_sql_query("CREATE TABLE IF NOT EXISTS PodcastTracktable_New(".
"PODTrackindex INTEGER PRIMARY KEY NOT NULL UNIQUE, ".
"JustUpdated TINYINT(1), ".
"PODindex INTEGER, ".
"Title VARCHAR(255), ".
"Artist VARCHAR(255), ".
"Duration INTEGER, ".
"PubDate INTEGER, ".
"FileSize INTEGER, ".
"Description TEXT, ".
"Link TEXT, ".
"Guid TEXT, ".
"Localfilename VARCHAR(255), ".
"Downloaded TINYINT(1) DEFAULT 0, ".
"Listened TINYINT(1) DEFAULT 0, ".
"New TINYINT(1) DEFAULT 1, ".
"Progress INTEGER DEFAULT 0, ".
"Deleted TINYINT(1) DEFAULT 0)", true);
generic_sql_query("INSERT INTO PodcastTracktable_New SELECT PODTrackindex, JustUpdated, PODindex, Title, Artist,
Duration, PubDate, FileSize, Description, Link, Guid, Localfilename, Downloaded, Listened, New, 0 AS Progress, Deleted
FROM PodcastTracktable", true);
generic_sql_query("DROP TABLE PodcastTracktable", true);
generic_sql_query("ALTER TABLE PodcastTracktable_New RENAME TO PodcastTracktable", true);
generic_sql_query("CREATE INDEX IF NOT EXISTS ptt ON PodcastTracktable (Title)", true);
generic_sql_query("UPDATE Statstable SET Value = 42 WHERE Item = 'SchemaVer'", true);
break;
case 42:
logger::log("SQL", "Updating FROM Schema version 42 TO Schema version 43");
update_stream_images(43);
generic_sql_query("UPDATE Statstable SET Value = 43 WHERE Item = 'SchemaVer'", true);
break;
case 43:
logger::log("SQL", "Updating FROM Schema version 43 TO Schema version 44");
empty_modified_cache_dirs(44);
generic_sql_query("UPDATE Statstable SET Value = 44 WHERE Item = 'SchemaVer'", true);
break;
case 44:
logger::log("SQL", "Updating FROM Schema version 44 TO Schema version 45");
upgrade_host_defs(45);
generic_sql_query("UPDATE Statstable SET Value = 45 WHERE Item = 'SchemaVer'", true);
break;
case 45:
logger::log("SQL", "Updating FROM Schema version 45 TO Schema version 46");
generic_sql_query("UPDATE Statstable SET Value = 46 WHERE Item = 'SchemaVer'", true);
break;
case 46:
logger::log("SQL", "Updating FROM Schema version 46 TO Schema version 47");
generic_sql_query("ALTER TABLE Playcounttable ADD COLUMN SyncCount INT UNSIGNED DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 47 WHERE Item = 'SchemaVer'", true);
create_playcount_triggers();
break;
case 47:
logger::log("SQL", "Updating FROM Schema version 47 TO Schema version 48");
generic_sql_query("UPDATE Statstable SET Value = 48 WHERE Item = 'SchemaVer'", true);
break;
case 48:
logger::log("SQL", "Updating FROM Schema version 48 TO Schema version 49");
upgrade_host_defs(49);
generic_sql_query("UPDATE Statstable SET Value = 49 WHERE Item = 'SchemaVer'", true);
break;
case 49:
logger::log("SQL", "Updating FROM Schema version 49 TO Schema version 50");
generic_sql_query("ALTER TABLE Tracktable ADD COLUMN LinkChecked TINYINT(1) DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 50 WHERE Item = 'SchemaVer'", true);
break;
case 50:
logger::log("SQL", "Updating FROM Schema version 50 TO Schema version 51");
generic_sql_query("UPDATE Statstable SET Value = 51 WHERE Item = 'SchemaVer'", true);
break;
case 51:
logger::log("SQL", "Updating FROM Schema version 51 TO Schema version 52");
generic_sql_query("ALTER TABLE Tracktable ADD COLUMN isAudiobook TINYINT(1) DEFAULT 0", true);
generic_sql_query("UPDATE Statstable SET Value = 52 WHERE Item = 'SchemaVer'", true);
break;
case 52:
logger::log("SQL", "Updating FROM Schema version 52 TO Schema version 53");
create_progress_triggers();
generic_sql_query("UPDATE Statstable SET Value = 53 WHERE Item = 'SchemaVer'", true);
break;
case 53:
logger::log("SQL", "Updating FROM Schema version 53 TO Schema version 54");
require_once ('utils/backgroundimages.php');
first_upgrade_of_user_backgrounds();
generic_sql_query("UPDATE Statstable SET Value = 54 WHERE Item = 'SchemaVer'", true);
break;
case 54:
logger::log("SQL", "Updating FROM Schema version 54 TO Schema version 55");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('PodUpPid', 0)", true);
generic_sql_query("UPDATE Statstable SET Value = 55 WHERE Item = 'SchemaVer'", true);
break;
case 55:
logger::log("SQL", "Updating FROM Schema version 55 TO Schema version 56");
generic_sql_query("INSERT INTO Statstable (Item, Value) VALUES ('Updating', '0')", true);
generic_sql_query("UPDATE Statstable SET Value = 56 WHERE Item = 'SchemaVer'", true);
break;
}
$sv++;
}
return array(true, "");
}
function delete_oldtracks() {
// generic_sql_query("DROP TABLE IF EXISTS OldTracks", true);
// generic_sql_query("CREATE TEMPORARY TABLE OldTracks AS SELECT TTindex FROM Tracktable JOIN Playcounttable USING (TTindex) WHERE Hidden = 1 AND DATETIME('now', '-6 MONTH') > DateAdded AND Playcount < 2", true);
// generic_sql_query("DELETE FROM Tracktable WHERE TTindex IN (SELECT TTindex FROM OldTracks)", true);
}
function delete_orphaned_artists() {
generic_sql_query("DROP TABLE IF EXISTS Croft", true);
generic_sql_query("CREATE TEMPORARY TABLE Croft AS SELECT Artistindex FROM Tracktable UNION SELECT AlbumArtistindex FROM Albumtable", true);
generic_sql_query("DELETE FROM Artisttable WHERE Artistindex NOT IN (SELECT Artistindex FROM Croft)", true);
}
function hide_played_tracks() {
generic_sql_query("UPDATE Tracktable SET Hidden = 1, isSearchResult = 0 WHERE TTindex IN (SELECT TTindex FROM Tracktable JOIN Playcounttable USING (TTindex) WHERE isSearchResult = 2)", true);
}
function sql_recent_tracks() {
global $prefs;
$qstring = "SELECT TTindex FROM Tracktable WHERE DATETIME('now', '-2 MONTH') <= DATETIME(DateAdded) AND Hidden = 0 AND isSearchResult < 2 AND isAudiobook = 0 AND Uri IS NOT NULL";
if ($prefs['collection_player'] == 'mopidy' && $prefs['player_backend'] == 'mpd') {
$qstring .= ' AND Uri LIKE "local:%"';
}
return $qstring . " ORDER BY RANDOM()";
}
function sql_recent_albums() {
global $prefs;
$qstring = "SELECT TTindex, Albumindex, TrackNo FROM Tracktable WHERE DATETIME('now', '-2 MONTH') <= DATETIME(DateAdded) AND Hidden = 0 AND isSearchResult < 2 AND isAudiobook = 0 AND Uri IS NOT NULL";
if ($prefs['collection_player'] == 'mopidy' && $prefs['player_backend'] == 'mpd') {
$qstring .= ' AND Uri LIKE "local:%"';
}
return $qstring;
}
function sql_recently_played() {
return "SELECT t.Uri, t.Title, a.Artistname, al.Albumname, al.Image, al.ImgKey, CAST(strftime('%s', p.LastPlayed) AS INT) AS unixtime FROM Tracktable AS t JOIN Playcounttable AS p USING (TTindex) JOIN Albumtable AS al USING (albumindex) JOIN Artisttable AS a ON (a.Artistindex = al.AlbumArtistindex) WHERE DATETIME('now', '-14 DAYS') <= DATETIME(p.LastPlayed) AND p.LastPlayed IS NOT NULL ORDER BY p.LastPlayed DESC";
}
function recently_played_playlist() {
$qstring = "SELECT TTindex FROM Playcounttable JOIN Tracktable USING (TTindex) WHERE DATETIME('now', '-14 DAYS') <= DATETIME(LastPlayed) AND LastPlayed IS NOT NULL AND isAudiobook = 0 AND Hidden = 0";
return $qstring;
}
function sql_two_weeks() {
return "DATETIME('now', '-14 DAYS') > DATETIME(LastPlayed)";
}
function sql_two_weeks_include($days) {
return "DATETIME('now', '-".$days." DAYS') <= DATETIME(LastPlayed) AND LastPlayed IS NOT NULL";
}
function sql_to_unixtime($s) {
return "CAST(strftime('%s', ".$s.") AS INT)";
}
function track_date_check($range, $flag) {
if ($flag == 'b') {
return '';
}
switch ($range) {
case ADDED_ALL_TIME:
return '';
break;
case ADDED_TODAY:
return "AND DATETIME('now', '-1 DAYS') <= DateAdded";
break;
case ADDED_THIS_WEEK:
return "AND DATETIME('now', '-7 DAYS') <= DateAdded";
break;
case ADDED_THIS_MONTH:
return "AND DATETIME('now', '-1 MONTHS') <= DateAdded";
break;
case ADDED_THIS_YEAR:
return "AND DATETIME('now', '-1 YEAR') <= DateAdded";
break;
default:
logger::error("SQL", "ERROR! Unknown Collection Range ".$range);
return '';
break;
}
}
function create_conditional_triggers() {
generic_sql_query("CREATE TRIGGER track_insert_trigger AFTER INSERT ON Tracktable
FOR EACH ROW
WHEN NEW.Hidden=0
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = NEW.Albumindex;
END;", true);
generic_sql_query("CREATE TRIGGER track_update_trigger AFTER UPDATE ON Tracktable
FOR EACH ROW
WHEN NEW.Hidden=0
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = NEW.Albumindex;
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = OLD.Albumindex;
END;", true);
}
function create_playcount_triggers() {
generic_sql_query("CREATE TRIGGER syncupdatetrigger AFTER UPDATE ON Playcounttable
FOR EACH ROW
WHEN NEW.Playcount > OLD.Playcount
BEGIN
UPDATE Playcounttable SET SyncCount = OLD.SyncCount + 1 WHERE TTindex = New.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER syncinserttrigger AFTER INSERT ON Playcounttable
FOR EACH ROW
BEGIN
UPDATE Playcounttable SET SyncCount = 1 WHERE TTindex = NEW.TTindex;
END;", true);
}
function create_update_triggers() {
logger::trace("SQLITE", "Creating Triggers for update operation");
generic_sql_query("CREATE TRIGGER rating_update_trigger AFTER UPDATE ON Ratingtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER rating_insert_trigger AFTER INSERT ON Ratingtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_delete_trigger AFTER DELETE ON Tagtable
FOR EACH ROW
BEGIN
DELETE FROM TagListtable WHERE Tagindex = OLD.Tagindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_insert_trigger AFTER INSERT ON TagListtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
UPDATE Tracktable SET Hidden = 0, justAdded = 1 WHERE Hidden = 1 AND TTindex = NEW.TTindex;
UPDATE Tracktable SET isSearchResult = 1, LastModified = NULL, justAdded = 1 WHERE isSearchResult > 1 AND TTindex = NEW.TTindex;
END;", true);
generic_sql_query("CREATE TRIGGER tag_remove_trigger AFTER DELETE ON TagListtable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = OLD.TTindex);
END;", true);
generic_sql_query("CREATE TRIGGER track_delete_trigger AFTER DELETE ON Tracktable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = OLD.Albumindex;
END;", true);
}
function create_progress_triggers() {
generic_sql_query("CREATE TRIGGER progress_update_trigger AFTER UPDATE ON Progresstable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
END;", true);
generic_sql_query("CREATE TRIGGER progress_insert_trigger AFTER INSERT ON Progresstable
FOR EACH ROW
BEGIN
UPDATE Albumtable SET justUpdated = 1 WHERE Albumindex = (SELECT Albumindex FROM Tracktable WHERE TTindex = NEW.TTindex);
END;", true);
}
?>

View File

@ -0,0 +1,631 @@
<?php
chdir('../..');
require_once ("includes/vars.php");
require_once ("includes/functions.php");
require_once ("utils/imagefunctions.php");
require_once ("international.php");
logger::blurt("USERRATING", "--------------------------START---------------------");
require_once ("backends/sql/backend.php");
require_once ("backends/sql/metadatafunctions.php");
require_once ("player/".$prefs['player_backend']."/player.php");
$error = 0;
$count = 1;
$divtype = "album1";
$returninfo = array();
$download_file = "";
$dummydata = array('dummy' => 'baby');
if ($mysqlc == null) {
logger::error("RATINGS", "Can't Do ratings stuff as no SQL connection!");
header('HTTP/1.0 403 Forbidden');
exit(0);
}
$start = time();
open_transaction();
create_foundtracks();
$took = time() - $start;
logger::debug("TIMINGS", "Creating FoundTracks took ".$took." seconds");
$params = json_decode(file_get_contents('php://input'), true);
// If you add new actions remember to update actions_requring_cleanup in metahandlers.js
foreach($params as $p) {
romprmetadata::sanitise_data($p);
logger::mark("USERRATING", "Doing action",strtoupper($p['action']));
foreach ($p as $i => $v) {
if ($i != 'action' && $v) {
logger::log(" Parameter", $i,':',$v);
}
}
switch ($p['action']) {
case 'dummy':
$returninfo = $dummydata;
break;
case 'getplaylist':
preparePlaylist();
case 'repopulate':
$returninfo = doPlaylist($p['playlist'], $p['numtracks']);
break;
case 'ratlist':
$returninfo = get_rating_headers($p['sortby']);
break;
case 'ratentries':
$returninfo = get_rating_info($p['sortby'], $p['value']);
break;
case 'metabackup':
backup_unrecoverable_data();
$returninfo = $dummydata;
break;
case 'getbackupdata':
$returninfo = analyse_backups();
break;
case 'backupremove':
removeBackup($p['which']);
$returninfo = $dummydata;
break;
case 'backuprestore':
restoreBackup($p['which']);
$returninfo = $dummydata;
break;
case 'gettags':
$returninfo = list_tags();
break;
case 'getfaveartists':
$returninfo = get_fave_artists();
break;
case 'getrecommendationseeds';
$returninfo = get_recommendation_seeds($p['days'], $p['limit'], $p['top']);
break;
case 'addtolistenlater':
addToListenLater($p['json']);
$returninfo = $dummydata;
break;
case 'getlistenlater':
$returninfo = getListenLater();
break;
case 'removelistenlater':
removeListenLater($p['index']);
$returninfo = $dummydata;
break;
case 'getlinktocheck':
$returninfo = getLinkToCheck();
break;
case 'updatelinkcheck':
updateCheckedLink($p['ttindex'], $p['uri'], $p['status']);
$returninfo = $dummydata;
break;
case 'resetlinkcheck':
resetLinkCheck();
$returninfo = $dummydata;
break;
case 'get':
case 'inc':
case "add":
case 'set':
case 'remove':
case 'cleanup':
case 'amendalbum':
case 'deletetag':
case 'delete':
case 'deletewl':
case 'getcharts':
case 'clearwishlist':
case 'setalbummbid':
case 'syncinc':
case 'resetallsyncdata':
case 'deleteid':
romprmetadata::{$p['action']}($p);
break;
default:
logger::fail("USERRATINGS", "Unknown Request",$p['action']);
header('HTTP/1.1 400 Bad Request');
break;
}
check_transaction();
}
if (count($returninfo) == 0 || array_key_exists('metadata', $returninfo)) {
// We don't do a database cleanup here. It can take a long time and this
// really slows the GUI down. Cleanups and stats updates are done out-of-band
// by the frontend DB Queue manager by calling into here with action = cleanup
prepare_returninfo();
}
print json_encode($returninfo);
close_transaction();
logger::blurt("USERRATING", "---------------------------END----------------------");
function prepare_returninfo() {
logger::log("USERRATINGS", "Preparing Return Info");
global $returninfo, $prefs;
$t = microtime(true);
$result = generic_sql_query('SELECT DISTINCT AlbumArtistindex FROM Albumtable WHERE justUpdated = 1');
foreach ($result as $mod) {
if (artist_albumcount($mod['AlbumArtistindex']) == 0) {
$returninfo['deletedartists'][] = $mod['AlbumArtistindex'];
logger::mark("USERRATINGS", " Artist ".$mod['AlbumArtistindex']." has no visible albums");
} else {
logger::mark("USERRATINGS", " Artist ".$mod['AlbumArtistindex']." has modified albums");
switch ($prefs['sortcollectionby']) {
case 'album':
break;
case 'artist':
logger::trace("USERRATINGS", " Creating Artist Header");
$returninfo['modifiedartists'][] = do_artists_from_database('a', $prefs['sortcollectionby'], $mod['AlbumArtistindex']);
break;
case 'albumbyartist':
if ($prefs['showartistbanners']) {
logger::trace("USERRATINGS", " Creating Artist Banner");
$returninfo['modifiedartists'][] = do_artist_banner('a','album',$mod['AlbumArtistindex']);
}
break;
}
}
}
$at = microtime(true) - $t;
logger::debug("TIMINGS", " -- Finding modified artists took ".$at." seconds");
$t = microtime(true);
$result = generic_sql_query('SELECT Albumindex, AlbumArtistindex FROM Albumtable WHERE justUpdated = 1');
foreach ($result as $mod) {
if (album_trackcount($mod['Albumindex']) == 0) {
logger::mark("USERRATINGS", " Album ".$mod['Albumindex']." has no visible tracks");
$returninfo['deletedalbums'][] = $mod['Albumindex'];
} else {
logger::mark("USERRATINGS", " Album ".$mod['Albumindex']." was modified");
$prefix = check_album_is_album($mod['Albumindex']);
switch ($prefs['sortcollectionby']) {
case 'album':
case 'albumbyartist':
$r = do_albums_from_database($prefix, 'album', 'root', $mod['Albumindex'], false, false);
break;
case 'artist':
$r = do_albums_from_database($prefix, 'album', $mod['AlbumArtistindex'], $mod['Albumindex'], false, false);
break;
}
$r['tracklist'] = do_tracks_from_database($prefix, 'album', $mod['Albumindex'], true);
$returninfo['modifiedalbums'][] = $r;
}
}
$at = microtime(true) - $t;
logger::debug("TIMINGS", " -- Finding modified albums took ".$at." seconds");
$t = microtime(true);
$result = generic_sql_query('SELECT Albumindex, AlbumArtistindex, Uri, TTindex FROM Tracktable JOIN Albumtable USING (Albumindex) WHERE justAdded = 1 AND Hidden = 0');
foreach ($result as $mod) {
logger::log("USERRATING", " New Track in album ".$mod['Albumindex'].' has TTindex '.$mod['TTindex']);
$returninfo['addedtracks'][] = array('artistindex' => $mod['AlbumArtistindex'], 'albumindex' => $mod['Albumindex'], 'trackuri' => rawurlencode($mod['Uri']));
}
$at = microtime(true) - $t;
logger::debug("TIMINGS", " -- Finding added tracks took ".$at." seconds");
}
function check_album_is_album($albumindex) {
// See if the album is an album or an audiobook
$c = simple_query('COUNT(TTindex)', 'Tracktable', 'isAudiobook = 1 AND Albumindex', $albumindex, 0);
if ($c > 0) {
logger::trace("USERRATINGS", " This is an audiobook");
return 'z';
} else {
return 'a';
}
}
function artist_albumcount($artistindex) {
return generic_sql_query(
"SELECT
COUNT(Albumindex) AS num
FROM
Albumtable LEFT JOIN Tracktable USING (Albumindex)
WHERE
AlbumArtistindex = ".$artistindex.
" AND Hidden = 0
AND isSearchResult < 2
AND Uri IS NOT NULL", false, null, 'num', 0);
}
function album_trackcount($albumindex) {
return generic_sql_query(
"SELECT
COUNT(TTindex) AS num
FROM
Tracktable
WHERE
Albumindex = ".$albumindex.
" AND Hidden = 0
AND isSearchResult < 2
AND Uri IS NOT NULL", false, null, 'num', 0);
}
function doCollectionHeader() {
global $returninfo;
$returninfo['stats'] = collectionStats();
}
function check_backup_dir() {
$dirname = date('Y-m-d-H-i');
if (is_dir('prefs/databackups/'.$dirname)) {
rrmdir('prefs/databackups/'.$dirname);
}
mkdir('prefs/databackups/'.$dirname, 0755);
return 'prefs/databackups/'.$dirname;
}
function backup_unrecoverable_data() {
// This makes a backup of all manually added tracks and all
// rating, tag, and playcount data. This can be used to restore it
// or transfer it to another machine
$dirname = check_backup_dir();
logger::log("BACKEND", "Backing up manually added tracks");
$tracks = get_manually_added_tracks();
file_put_contents($dirname.'/tracks.json',json_encode($tracks));
logger::log("BACKEND", "Backing up ratings");
$tracks = get_ratings();
file_put_contents($dirname.'/ratings.json',json_encode($tracks));
logger::log("BACKEND", "Backing up Playcounts");
$tracks = get_playcounts();
file_put_contents($dirname.'/playcounts.json',json_encode($tracks));
logger::log("BACKEND", "Backing up Tags");
$tracks = get_tags();
file_put_contents($dirname.'/tags.json',json_encode($tracks));
}
function analyse_backups() {
$data = array();
$bs = glob('prefs/databackups/*');
rsort($bs);
foreach ($bs as $backup) {
// This is nice data to have, but it takes a very long time on a moderate computer
// FIXME: We should create these numbers when we create the backup and save them so we can read them in
// $tracks = count(json_decode(file_get_contents($backup.'/tracks.json')));
// $ratings = count(json_decode(file_get_contents($backup.'/ratings.json')));
// $playcounts = count(json_decode(file_get_contents($backup.'/playcounts.json')));
// $tags = count(json_decode(file_get_contents($backup.'/tags.json')));
$data[] = array(
'dir' => basename($backup),
'name' => strftime('%c', DateTime::createFromFormat('Y-m-d-H-i', basename($backup))->getTimestamp()),
'stats' => array(
'Manually Added Tracks' => file_exists($backup.'/tracks.json') ? 'OK' : 'Missing!',
'Playcounts' => file_exists($backup.'/playcounts.json') ? 'OK' : 'Missing!',
'Tracks With Ratings' => file_exists($backup.'/ratings.json') ? 'OK' : 'Missing!',
'Tracks With Tags' => file_exists($backup.'/tags.json') ? 'OK' : 'Missing!',
)
);
}
return $data;
}
function removeBackup($which) {
rrmdir('prefs/databackups/'.$which);
}
function restoreBackup($backup) {
global $prefs;
if (file_exists('prefs/backupmonitor')) {
unlink('prefs/backupmonitor');
}
$monitor = fopen('prefs/backupmonitor', 'w');
if (file_exists('prefs/databackups/'.$backup.'/tracks.json')) {
logger::mark("BACKUPS", "Restoring Manually Added Tracks");
$tracks = json_decode(file_get_contents('prefs/databackups/'.$backup.'/tracks.json'), true);
foreach ($tracks as $i => $trackdata) {
romprmetadata::sanitise_data($trackdata);
romprmetadata::add($trackdata, false);
$progress = round(($i/count($tracks))*100);
fwrite($monitor, "\n<b>Restoring Manually Added Tracks : </b>".$progress."%");
}
}
if (file_exists('prefs/databackups/'.$backup.'/ratings.json')) {
logger::mark("BACKUPS", "Restoring Ratings");
$tracks = json_decode(file_get_contents('prefs/databackups/'.$backup.'/ratings.json'), true);
foreach ($tracks as $i => $trackdata) {
romprmetadata::sanitise_data($trackdata);
$trackdata['attributes'] = array(array('attribute' => 'Rating', 'value' => $trackdata['rating']));
romprmetadata::set($trackdata, true);
$progress = round(($i/count($tracks))*100);
fwrite($monitor, "\n<b>Restoring Ratings : </b>".$progress."%");
}
}
if (file_exists('prefs/databackups/'.$backup.'/tags.json')) {
logger::mark("BACKUPS", "Restoring Tags");
$tracks = json_decode(file_get_contents('prefs/databackups/'.$backup.'/tags.json'), true);
foreach ($tracks as $i => $trackdata) {
romprmetadata::sanitise_data($trackdata);
$trackdata['attributes'] = array(array('attribute' => 'Tags', 'value' => explode(',',$trackdata['tag'])));
romprmetadata::set($trackdata, true);
$progress = round(($i/count($tracks))*100);
fwrite($monitor, "\n<b>Restoring Tags : </b>".$progress."%");
}
}
if (file_exists('prefs/databackups/'.$backup.'/playcounts.json')) {
logger::mark("BACKUPS", "Restoring Playcounts");
$tracks = json_decode(file_get_contents('prefs/databackups/'.$backup.'/playcounts.json'), true);
foreach ($tracks as $i => $trackdata) {
romprmetadata::sanitise_data($trackdata);
$trackdata['attributes'] = array(array('attribute' => 'Playcount', 'value' => $trackdata['playcount']));
if (!array_key_exists('lastplayed', $trackdata)) {
// Sanitise backups made before lastplayed was added
$trackdata['lastplayed'] = null;
}
romprmetadata::inc($trackdata);
$progress = round(($i/count($tracks))*100);
fwrite($monitor, "\n<b>Restoring Playcounts : </b>".$progress."%");
}
}
fwrite($monitor, "\n<b>Cleaning Up...</b>");
// Now... we may have restored data on tracks that were previously local and now aren't there any more.
// If they're local tracks that have been removed, then we don't want them or care about their data
if ($prefs['player_backend'] == "mpd") {
generic_sql_query("DELETE FROM Tracktable WHERE Uri IS NOT NULL AND LastModified IS NULL AND Hidden = 0", true);
} else {
generic_sql_query("DELETE FROM Tracktable WHERE Uri LIKE 'local:%' AND LastModified IS NULL AND Hidden = 0", true);
}
romprmetadata::resetallsyncdata();
remove_cruft();
update_track_stats();
fclose($monitor);
}
function get_manually_added_tracks() {
// get_manually_added_tracks
// Creates data for backup
return generic_sql_query(
"SELECT
Tracktable.Title AS title,
Tracktable.TrackNo AS trackno,
Tracktable.Duration AS duration,
Tracktable.Disc AS disc,
Tracktable.Uri AS uri,
Albumtable.Albumname AS album,
Albumtable.AlbumUri AS albumuri,
Albumtable.Year AS date,
ta.Artistname AS artist,
aat.Artistname AS albumartist
FROM
Tracktable
JOIN Artisttable AS ta USING (Artistindex)
JOIN Albumtable ON Tracktable.Albumindex = Albumtable.Albumindex
JOIN Artisttable AS aat ON Albumtable.AlbumArtistindex = aat.Artistindex
WHERE Tracktable.LastModified IS NULL AND Tracktable.Hidden = 0 AND Tracktable.isSearchResult < 2 AND uri IS NOT NULL");
}
function get_ratings() {
// get_ratings
// Creates data for backup
return generic_sql_query(
"SELECT
r.Rating AS rating,
tr.Title AS title,
tr.TrackNo AS trackno,
tr.Duration AS duration,
tr.Disc AS disc,
tr.Uri AS uri,
al.Albumname AS album,
al.AlbumUri AS albumuri,
al.Year AS date,
ta.Artistname AS artist,
aat.Artistname AS albumartist
FROM
Ratingtable AS r
JOIN Tracktable AS tr USING (TTindex)
JOIN Artisttable AS ta USING (Artistindex)
JOIN Albumtable AS al ON tr.Albumindex = al.Albumindex
JOIN Artisttable AS aat ON al.AlbumArtistindex = aat.Artistindex
WHERE rating > 0 AND tr.Hidden = 0 AND tr.isSearchResult < 2
ORDER BY rating, albumartist, album, trackno");
}
function get_playcounts() {
// get_playcounts
// Creates data for backup
return generic_sql_query(
"SELECT
p.Playcount AS playcount,
p.LastPlayed AS lastplayed,
tr.Title AS title,
tr.TrackNo AS trackno,
tr.Duration AS duration,
tr.Disc AS disc,
tr.Uri AS uri,
al.Albumname AS album,
al.AlbumUri AS albumuri,
al.Year AS date,
ta.Artistname AS artist,
aat.Artistname AS albumartist
FROM
Playcounttable AS p
JOIN Tracktable AS tr USING (TTindex)
JOIN Artisttable AS ta USING (Artistindex)
JOIN Albumtable AS al ON tr.Albumindex = al.Albumindex
JOIN Artisttable AS aat ON al.AlbumArtistindex = aat.Artistindex
WHERE playcount > 0
ORDER BY playcount, albumartist, album, trackno");
}
function get_tags() {
// get_tags
// Creates data for backup
return generic_sql_query(
"SELECT
".SQL_TAG_CONCAT."AS tag,
tr.Title AS title,
tr.TrackNo AS trackno,
tr.Duration AS duration,
tr.Disc AS disc,
tr.Uri AS uri,
al.Albumname AS album,
al.AlbumUri AS albumuri,
al.Year AS date,
ta.Artistname AS artist,
aat.Artistname AS albumartist
FROM
Tagtable AS t
JOIN TagListtable AS tl USING (Tagindex)
JOIN Tracktable AS tr ON tl.TTindex = tr.TTindex
JOIN Artisttable AS ta USING (Artistindex)
JOIN Albumtable AS al ON tr.Albumindex = al.Albumindex
JOIN Artisttable AS aat ON al.AlbumArtistindex = aat.Artistindex
WHERE tr.Hidden = 0 AND tr.isSearchResult < 2
GROUP BY tr.TTindex");
}
function get_recommendation_seeds($days, $limit, $top) {
// 1. Get a list of tracks played in the last $days days, sorted by their OVERALL popularity
$resultset = generic_sql_query(
"SELECT SUM(Playcount) AS playtotal, Artistname, Title, Uri
FROM Playcounttable JOIN Tracktable USING (TTindex)
JOIN Artisttable USING (Artistindex)
WHERE ".sql_two_weeks_include($days).
" AND Uri IS NOT NULL GROUP BY Artistindex ORDER BY playtotal DESC LIMIT ".$limit);
// 2. Get a list of recently played tracks, ignoring popularity
// $result = generic_sql_query(
// "SELECT 0 AS playtotal, Artistname, Title, Uri
// FROM Playcounttable JOIN Tracktable USING (TTindex)
// JOIN Artisttable USING (Artistindex)
// WHERE ".sql_two_weeks_include(intval($days/2)).
// " AND Uri IS NOT NULL GROUP BY Artistindex ORDER BY ".SQL_RANDOM_SORT." LIMIT ".intval($limit/2));
// $resultset = array_merge($resultset, $result);
// 3. Get the top tracks overall
$tracks = get_track_charts(intval($limit/2));
foreach ($tracks as $track) {
if ($track['uri']) {
$resultset[] = array('playtotal' => $track['soundcloud_plays'],
'Artistname' => $track['label_artist'],
'Title' => $track['label_track'],
'Uri' => $track['uri']);
}
}
// 4. Randomise that list and return the first $top.
shuffle($resultset);
return array_slice($resultset,0,$top);
}
function get_fave_artists() {
// Can we have a tuning slider to increase the 'Playcount > x' value?
generic_sql_query(
"CREATE TEMPORARY TABLE aplaytable AS SELECT SUM(Playcount) AS playtotal, Artistindex FROM
(SELECT Playcount, Artistindex FROM Playcounttable JOIN Tracktable USING (TTindex) WHERE
Playcount > 10) AS derived GROUP BY Artistindex", true);
$artists = array();
$result = generic_sql_query(
"SELECT playtot, Artistname FROM (SELECT SUM(Playcount) AS playtot, Artistindex FROM
(SELECT Playcount, Artistindex FROM Playcounttable JOIN Tracktable USING (TTindex)) AS
derived GROUP BY Artistindex) AS alias JOIN Artisttable USING (Artistindex) WHERE
playtot > (SELECT AVG(playtotal) FROM aplaytable) ORDER BY ".SQL_RANDOM_SORT, false, PDO::FETCH_OBJ);
foreach ($result as $obj) {
logger::log("FAVEARTISTS", "Artist :",$obj->Artistname);
$artists[] = array( 'name' => $obj->Artistname, 'plays' => $obj->playtot);
}
return $artists;
}
function addToListenLater($album) {
$newid = spotifyAlbumId($album);
$result = generic_sql_query("SELECT * FROM AlbumsToListenTotable");
foreach ($result as $r) {
$d = json_decode($r['JsonData'], true);
$thisid = spotifyAlbumId($d);
if ($thisid == $newid) {
logger::warn("LISTENLATER", "Trying to add duplicate album to Listen Later");
return;
}
}
$d = json_encode($album);
sql_prepare_query(true, null, null, null, "INSERT INTO AlbumsToListenTotable (JsonData) VALUES (?)", $d);
}
function getListenLater() {
$result = generic_sql_query("SELECT * FROM AlbumsToListenTotable");
$retval = array();
foreach ($result as $r) {
$d = json_decode($r['JsonData']);
$d->rompr_index = $r['Listenindex'];
$retval[] = $d;
}
return $retval;
}
function removeListenLater($id) {
generic_sql_query("DELETE FROM AlbumsToListenTotable WHERE Listenindex = ".$id, true);
}
function spotifyAlbumId($album) {
if (array_key_exists('album', $album)) {
return $album['album']['id'];
} else {
return $album['id'];
}
}
function getLinkToCheck() {
// LinkChecked:
// 0 = Not Checked, Assumed Playable or Playable at last check
// 1 = Not Checked, Unplayable at last check
// 2 = Checked, Playable
// 3 = Checked, Unplayable
return generic_sql_query("SELECT TTindex, Uri, LinkChecked FROM Tracktable WHERE Uri LIKE 'spotify:%' AND Hidden = 0 AND isSearchResult < 2 AND LinkChecked < 2 ORDER BY TTindex ASC LIMIT 25");
}
function updateCheckedLink($ttindex, $uri, $status) {
logger::log("METADATA", "Updating Link Check For TTindex",$ttindex,$uri);
sql_prepare_query(true, null, null, null,
"UPDATE Tracktable SET LinkChecked = ?, Uri = ? WHERE TTindex = ?", $status, $uri, $ttindex);
}
function resetLinkCheck() {
generic_sql_query("UPDATE Tracktable SET LinkChecked = 0 WHERE LinkChecked = 2");
generic_sql_query("UPDATE Tracktable SET LinkChecked = 1 WHERE LinkChecked = 3");
}
?>

133
www/jukebox/backimage.php Normal file
View File

@ -0,0 +1,133 @@
<?php
ob_start();
include ("includes/vars.php");
include ("includes/functions.php");
include ("utils/backgroundimages.php");
include ("backends/sql/backend.php");
foreach($_REQUEST as $i => $r) {
logger::log("BACKIMAGE", $i,'=',$r);
}
$retval = array();
if (array_key_exists('getbackground', $_REQUEST)) {
$images = sql_prepare_query(false, PDO::FETCH_ASSOC, null, null, 'SELECT * FROM BackgroundImageTable WHERE Skin = ? AND BrowserID = ?', $_REQUEST['getbackground'], $_REQUEST['browser_id']);
$thisbrowseronly = true;
if (count($images) == 0) {
logger::log("BACKIMAGE", "No Custom Backgrounds Exist for",$_REQUEST['getbackground'],$_REQUEST['browser_id']);
$images = sql_prepare_query(false, PDO::FETCH_ASSOC, null, null, 'SELECT * FROM BackgroundImageTable WHERE Skin = ? AND BrowserID IS NULL', $_REQUEST['getbackground']);
$thisbrowseronly = false;
} else {
logger::log("BACKIMAGE", "Custom Backgrounds Exist for",$_REQUEST['getbackground'],$_REQUEST['browser_id']);
}
if (count($images) > 0) {
logger::log("BACKIMAGE", "Custom Backgrounds Exist for",$_REQUEST['getbackground']);
$retval = array('images' => array('portrait' => array(), 'landscape' => array()), 'thisbrowseronly' => $thisbrowseronly);
foreach ($images as $image) {
if ($image['Orientation'] == ORIENTATION_PORTRAIT) {
$retval['images']['portrait'][] = $image['Filename'];
} else {
$retval['images']['landscape'][] = $image['Filename'];
}
}
}
} else if (array_key_exists('clearbackground', $_REQUEST)) {
sql_prepare_query(true, null, null, null, 'DELETE FROM BackgroundImageTable WHERE Filename = ?', $_REQUEST['clearbackground']);
unlink($_REQUEST['clearbackground']);
if (is_numeric(basename(dirname($_REQUEST['clearbackground'])))) {
check_empty_directory(dirname('clearbackground'));
}
} else if (array_key_exists('clearallbackgrounds', $_REQUEST)) {
// Remove these here, just in case the folder has been deleted for some reason
sql_prepare_query(true, null, null, null, 'DELETE FROM BackgroundImageTable WHERE Skin = ? AND BrowserID = ?', $_REQUEST['clearallbackgrounds'], $_REQUEST['browser_id']);
if (is_dir('prefs/userbackgrounds/'.$_REQUEST['clearallbackgrounds'].'/'.$_REQUEST['browser_id'])) {
logger::log("BACKIMAGE", "Removing All Backgrounds For ".$_REQUEST['clearallbackgrounds'].'/'.$_REQUEST['browser_id']);
delete_files('prefs/userbackgrounds/'.$_REQUEST['clearallbackgrounds'].'/'.$_REQUEST['browser_id']);
check_empty_directory('prefs/userbackgrounds/'.$_REQUEST['clearallbackgrounds'].'/'.$_REQUEST['browser_id']);
} else if (is_dir('prefs/userbackgrounds/'.$_REQUEST['clearallbackgrounds'])) {
logger::log("BACKIMAGE", "Removing All Backgrounds For ".$_REQUEST['clearallbackgrounds']);
delete_files('prefs/userbackgrounds/'.$_REQUEST['clearallbackgrounds']);
sql_prepare_query(true, null, null, null, 'DELETE FROM BackgroundImageTable WHERE Skin = ? AND BrowserID IS NULL', $_REQUEST['clearallbackgrounds']);
}
} else {
if (!array_key_exists('currbackground', $_REQUEST) || !array_key_exists('imagefile', $_FILES)) {
if (isset($_SERVER["CONTENT_LENGTH"])) {
if ($_SERVER["CONTENT_LENGTH"] > ((int) ini_get('post_max_size')*1024*1024)) {
logger::warn("BACKIMAGE", "Content Length Error");
header("HTTP/1.1 400 Bad Request", 'BACKIMAGE');
ob_flush();
exit(0);
}
}
logger::warn("BACKIMAGE", "Some kind of upload error");
header("HTTP/1.1 500 Internal Server Error");
ob_flush();
exit(0);
}
$skin = $_REQUEST['currbackground'];
$base = $skin;
$browserid = null;
if (array_key_exists('thisbrowseronly', $_REQUEST)) {
$base .= '/'.$_REQUEST['browser_id'];
$browserid = $_REQUEST['browser_id'];
}
$files = make_files_useful($_FILES['imagefile']);
foreach ($files as $filedata) {
$file = $filedata['name'];
logger::log("BACKIMAGE", "Uploading File ".$file);
$fname = format_for_url(format_for_disc(basename($file)));
$download_file = get_user_file($file, $fname, $filedata['tmp_name']);
if (!is_dir('prefs/userbackgrounds/'.$base)) {
mkdir('prefs/userbackgrounds/'.$base, 0755, true);
}
$file = 'prefs/userbackgrounds/'.$base.'/'.$fname;
if (file_exists($file)) {
logger::trace("BACKIMAGE", "Image",$file,"already exists");
unlink($download_file);
} else {
rename($download_file, $file);
$orientation = analyze_background_image($file);
sql_prepare_query(true, null, null, null, 'INSERT INTO BackgroundImageTable (Skin, BrowserID, Filename, Orientation) VALUES (?, ?, ?, ?)', $skin, $browserid, $file, $orientation);
}
}
}
print json_encode($retval);
ob_flush();
function check_empty_directory($dir) {
if (is_dir($dir) && !(new FilesystemIterator($dir))->valid()) {
rmdir($dir);
}
}
function delete_files($path, $expr = '*.*') {
// Prevents file not found or could not stat errors
$f = glob($path.'/'.$expr);
foreach ($f as $file) {
unlink($file);
}
}
function make_files_useful($arr) {
$new = array();
foreach ($arr as $key => $all) {
foreach ($all as $i => $val) {
$new[$i][$key] = $val;
}
}
return $new;
}
?>

View File

@ -0,0 +1,14 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
$uri = rawurldecode($_REQUEST['uri']);
$uri = 'http://'.$prefs['beets_server_location'].'/item/'.$uri;
logger::log("GETBEETSINFO", "Getting",$uri);
$d = new url_downloader(array('url' => $uri));
if ($d->get_data_to_string()) {
print $d->get_data();
} else {
header("HTTP/1.1 404 Not Found");
}
?>

View File

@ -0,0 +1,97 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
include ("getid3/getid3.php");
$fname = $_POST['file'];
$fname = preg_replace('/local:track:/','',$fname);
$fname = preg_replace('#file://#','',$fname);
$fname = 'prefs/MusicFolders/'.$fname;
$artist = $_POST['artist'];
$song = $_POST['song'];
$getID3 = new getID3;
$output = null;
logger::mark("LYRICS", "Looking for lyrics in",$fname);
logger::log("LYRICS", " Artist is",$artist);
logger::log("LYRICS", " Song is",$artist);
if (file_exists($fname)) {
logger::log("LYRICS", " File Exists");
$tags = $getID3->analyze($fname);
getid3_lib::CopyTagsToComments($tags);
if (array_key_exists('comments', $tags) &&
array_key_exists('lyrics', $tags['comments'])) {
$output = $tags['comments']['lyrics'][0];
} else if (array_key_exists('comments', $tags) &&
array_key_exists('unsynchronised_lyric', $tags['comments'])) {
$output = $tags['comments']['unsynchronised_lyric'][0];
} else if (array_key_exists('quicktime', $tags) &&
array_key_exists('moov', $tags['quicktime']) &&
array_key_exists('subatoms', $tags['quicktime']['moov'])) {
read_apple_awfulness($tags['quicktime']['moov']['subatoms']);
}
}
if ($output == null) {
$uri = "http://lyrics.wikia.com/api.php?func=getSong&artist=".urlencode($artist)."&song=".urlencode($song)."&fmt=xml";
logger::mark("LYRICS", "Trying",$uri);
$d = new url_downloader(array(
'url' => $uri,
'cache' => 'lyrics',
'return_data' => true
));
if ($d->get_data_to_file()) {
$l = simplexml_load_string($d->get_data());
if ($l->url) {
logger::log("LYRICS", " Now Getting",html_entity_decode($l->url));
$d2 = new url_downloader(array(
'url' => html_entity_decode($l->url),
'cache' => 'lyrics',
'return_data' => true
));
if ($d2->get_data_to_file()) {
if (preg_match('/\<div class=\'lyricbox\'\>\<script\>.*?\<\/script\>(.*?)\<\!--/', $d2->get_data(), $matches)) {
$output = html_entity_decode($matches[1]);
} else if (preg_match('/\<div class=\'lyricbox\'\>(.*?)\<div class=\'lyricsbreak\'\>/', $d2->get_data(), $matches)) {
$output = html_entity_decode($matches[1]);
} else {
logger::mark("LYRICS", " Could Not Find Lyrics");
}
}
} else {
logger::mark("LYRICS", " Nope, nothing there");
}
}
} else {
logger::mark("LYRICS", " Got lyrics from file");
}
if ($output == null) {
$output = '<h3 align=center>'.get_int_text("lyrics_nonefound").'</h3><p>'.get_int_text("lyrics_info").'</p>';
}
print $output;
function read_apple_awfulness($a) {
// Whoever came up with this was on something.
// All we want to do is read some metadata...
// why do you have to store it in such a horrible, horrible, way?
global $output;
foreach ($a as $atom) {
if (array_key_exists('name', $atom)) {
if (preg_match('/lyr$/', $atom['name'])) {
$output = preg_replace( '/^.*?data/', '', $atom['data']);
break;
}
}
if (array_key_exists('subatoms', $atom)) {
read_apple_awfulness($atom['subatoms']);
}
}
}
?>

View File

@ -0,0 +1,84 @@
<?php
chdir('../..');
ob_start();
include ("includes/vars.php");
include ("includes/functions.php");
if(array_key_exists("url", $_POST)) {
$link = get_bio_link($_POST['url']);
if ($link !== false) {
get_allmusic_page($link);
} else {
print '<p></p>';
}
} else {
header('HTTP/1.1 400 Bad Request');
}
ob_flush();
function get_bio_link($url) {
$html = '';
$d = new url_downloader(array(
'url' => $url,
'cache' => 'allmusic',
'return_data' => true
));
if ($d->get_data_to_file()) {
$DOM = new DOMDocument;
@$DOM->loadHTML($d->get_data());
$els = getElementsByClass($DOM, 'li', 'biography');
if (count($els) > 0) {
$e = $els[0];
$links = $e->GetElementsByTagName('a');
for ($i = 0; $i < $links->length; $i++) {
$link = $links->item($i)->getAttribute('href');
logger::log("AMBIO", "Found Bio Link",$link);
}
return 'http://www.allmusic.com'.$link;
} else {
return false;
}
} else {
return false;
}
}
function get_allmusic_page($url) {
logger::log("AMBIO", "Getting allmusic Page",$url);
$r = '<p></p>';
$d = new url_downloader(array(
'url' => $url,
'cache' => 'allmusic',
'return_data' => true
));
if ($d->get_data_to_file()) {
$DOM = new DOMDocument;
@$DOM->loadHTML($d->get_data());
$els = getElementsByClass($DOM, 'div', 'text');
foreach ($els as $el) {
$a = $el->getAttribute('itemprop');
if ($a == 'reviewBody') {
logger::trace("AMBIO", "Found Review Body");
$r = $el->nodeValue;
}
}
$r = '<p>'.$r.'</p><p>Biography courtesy of AllMusic</p>';
}
print preg_replace('/\n/', '</p><p>',$r);
}
function getElementsByClass(&$parentNode, $tagName, $className) {
$nodes=array();
$childNodeList = $parentNode->getElementsByTagName($tagName);
for ($i = 0; $i < $childNodeList->length; $i++) {
$temp = $childNodeList->item($i);
if (stripos($temp->getAttribute('class'), $className) !== false) {
$nodes[]=$temp;
}
}
return $nodes;
}
?>

View File

@ -0,0 +1,60 @@
<?php
chdir('../..');
ob_start();
include ("includes/vars.php");
include ("includes/functions.php");
if(array_key_exists("url", $_POST)) {
$link = get_bio_link($_POST['url']);
if ($link !== false) {
print $link;
} else {
header('HTTP/1.1 400 Bad Request');
}
} else {
header('HTTP/1.1 400 Bad Request');
}
ob_flush();
function get_bio_link($url) {
$html = '';
$d = new url_downloader(array(
'url' => $url,
'cache' => 'allmusic',
'return_data' => true
));
if ($d->get_data_to_file()) {
$DOM = new DOMDocument;
@$DOM->loadHTML($d->get_data());
$els = getElementsByClass($DOM, 'div', 'artist-contain');
if (count($els) > 0) {
$e = $els[0];
$links = $e->GetElementsByTagName('img');
for ($i = 0; $i < $links->length; $i++) {
$link = $links->item($i)->getAttribute('src');
logger::log("AMIMAGE", "Found Image",$link);
}
return $link;
} else {
return false;
}
} else {
return false;
}
}
function getElementsByClass(&$parentNode, $tagName, $className) {
$nodes=array();
$childNodeList = $parentNode->getElementsByTagName($tagName);
for ($i = 0; $i < $childNodeList->length; $i++) {
$temp = $childNodeList->item($i);
if (stripos($temp->getAttribute('class'), $className) !== false) {
$nodes[]=$temp;
}
}
return $nodes;
}
?>

View File

@ -0,0 +1,22 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$uri = $_POST['url'];
$params = array();
foreach ($_POST as $k => $v) {
if ($k != 'url') {
$params[] = $k.'='.rawurlencode($v);
}
}
$params[] = 'key=qmBviLdmIHhnxXkzWLHR';
$params[] = 'secret=KAtjSjsJJlfQjdCXUrnbyXAltXDfelaV';
if (count($params) > 0) {
$uri .= "?".implode('&', $params);
}
getCacheData($uri, 'discogs');
?>

View File

@ -0,0 +1,18 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$use_cache = $_POST['cache'] == 'true' ? true : false;
$params = array();
foreach ($_POST as $k => $v) {
if ($k != 'cache') {
$params[] = $k.'='.rawurlencode($v);
}
}
$url = "https://ws.audioscrobbler.com/2.0/?";
$url .= implode('&', $params);
getCacheData($url, 'lastfm', $use_cache);
?>

View File

@ -0,0 +1,19 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$uri = $_POST['url'];
$params = array();
foreach ($_POST as $k => $v) {
if ($k != 'url') {
$params[] = $k.'='.$v;
}
}
if (count($params) > 0) {
$uri .= "?".implode('&', $params);
}
getCacheData($uri, 'musicbrainz');
?>

View File

@ -0,0 +1,9 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$clientid = "6f43d0d67acd6635273ffd6eeed302aa";
$uri = $_POST['url'];
getCacheData('https://api.soundcloud.com/'.$uri.'?client_id='.$clientid, 'soundcloud', true);
?>

View File

@ -0,0 +1,29 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
include ("includes/spotifyauth.php");
$uri = $_POST['url'];
$cache = array_key_exists('cache', $_POST) ? true : false;
logger::mark("SPOTIFY", "Downloading",$uri);
logger::trace("SPOTIFY", " Cache is",$cache);
$filename = 'prefs/jsoncache/spotify/'.md5($uri);
if ($cache && file_exists($filename)) {
logger::log("SPOTIFY", "Returning cached data");
header("Pragma: From Cache");
print file_get_contents($filename);
} else {
list($success, $content, $status) = get_spotify_data($uri);
if ($success) {
file_put_contents($filename, $content);
header("Pragma: Not Cached");
print $content;
} else {
header('HTTP/1.1 '.$status.' '.http_status_code_string($status));
$r = array('error' => $status, 'message' => $content);
print json_encode($r);
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$uri = rawurldecode($_REQUEST['uri']);
logger::mark("GOOGLE", "Getting",$uri);
getCacheData($uri, 'google', true);
?>

View File

@ -0,0 +1,761 @@
<?php
chdir('../..');
include ("includes/vars.php");
include ("includes/functions.php");
include ("international.php");
$domain = "en";
$userdomain = false;
$mobile = (array_key_exists('layout', $_POST) && ($_POST['layout'] == 'phone' || $_POST['layout'] == 'tablet')) ? true : false;
// Switch off error reporting prevents us from having to repeatedly check
// that the objects we're foreaching on actually exist. Errors get dumped
// to stdout and mess up the xml response. We don't wanna see them.
// Remember to switch this off if debugging this script.
error_reporting(0);
if (array_key_exists("lang", $_POST)) {
$domain = $_POST["lang"];
}
logger::trace("WIKIPEDIA", "Using Language",$domain);
if (array_key_exists("wiki", $_POST)) {
// An intra-wiki link from a page we're displaying
$a = preg_match('#(.*?)/(.*)#', $_POST['wiki'], $matches);
send_result(get_wikipedia_page( $matches[2], $matches[1].".wikipedia.org", false ));
} else if (array_key_exists("uri", $_POST)) {
// Full URI to get - eg this will be a link found from musicbrainz
$uri = $_POST['uri'];
logger::log("WIKIPEDIA", "URI request ".$uri);
$a = preg_match('#https*://(.*?)/#', $uri, $matches);
$xml_response = get_wikipedia_page(basename($uri), $matches[1], true);
if ($userdomain == false) {
// Found a page, but not in the user's chosen domain
if (array_key_exists('term', $_POST)) {
logger::log("WIKIPEDIA", "Page was retreieved but not in user's chosen language. Checking via a search");
$upage = wikipedia_find_exact($_POST['term'], $domain);
if ($upage != '') {
$xml_response = $upage;
}
}
}
send_result($xml_response);
} else if (array_key_exists("artist", $_POST)) {
// Search for an artist
$xml_response = getArtistWiki($_POST['artist'], $_POST['disambiguation']);
if ($xml_response == null) {
send_failure($_POST['artist']);
} else {
send_result($xml_response);
}
} else if (array_key_exists("album", $_POST)) {
// Search for an album
logger::log("WIKIPEDIA", "Doing album ".$_POST['album']);
$xml_response = getAlbumWiki($_POST['album'], $_POST['albumartist']);
if ($xml_response == null) {
send_failure($_POST['album']);
} else {
send_result($xml_response);
}
} else if (array_key_exists("track", $_POST)) {
// Search for a track
logger::log("WIKIPEDIA", "Doing track ".$_POST['track']);
$xml_response = getTrackWiki($_POST['track'], $_POST['trackartist']);
if ($xml_response == null) {
send_failure($_POST['track']);
} else {
send_result($xml_response);
}
}
// ==========================================================================
//
// Getting stuff from wikipedia, including language munging
//
// ==========================================================================
function wikipedia_request($url) {
logger::trace("WIKIPEDIA", "Getting : ".$url);
$d = new url_downloader(array(
'url' => $url,
'cache' => 'wikipedia',
'return_data' => true
));
if ($d->get_data_to_file()) {
return $d->get_data();
} else {
return null;
}
}
function get_wikipedia_page($page, $site, $langsearch) {
// $page will be eg 'Air_(French_band)'
// $site will be eg 'en.wikipedia.org'
// $langsearch is true if we want to find a page in the user's language
// $domain is the language the user wants to use - eg 'fr'
global $domain;
global $userdomain;
global $mobile;
// $request_domain is the language of the page we've been asked to get
$r = preg_match("#(.*?)\.#", $site, $matches);
$request_domain = $matches[1];
$format_domain = $request_domain;
$req = "";
if ($langsearch) {
logger::log("WIKIPEDIA", "Request for page ".$page." from ".$site.". Domain is ".$request_domain." and user domain is ".$domain);
$user_link = ($request_domain == $domain) ? $page : null;
$english_link = ($site == "en.wikipedia.org") ? $page : null;
logger::log("WIKIPEDIA", "User Link is ".$user_link." and english link is ".$english_link);
if ($domain != $request_domain) {
logger::log("WIKIPEDIA", "Asked for page ".$page." from site ".$site." but user wants domain ".$domain);
// Find language links for the requested page
$langlinks = wikipedia_request("http://".$site."/w/api.php?action=query&prop=langlinks&titles=".$page."&format=xml");
if ($langlinks !== null) {
$langs = simplexml_load_string($langlinks);
if ($langs->query->pages->page->langlinks) {
foreach($langs->query->pages->page->langlinks->ll as $ll) {
$l = $ll['lang'];
$t = dom_import_simplexml($ll)->textContent;
logger::log("WIKIPEDIA", "Found language link ".$l." title ".$t);
if ($l == $domain) {
$user_link = preg_replace('/ /', '_', $t);
}
if ($l == "en" && $english_link == null) {
$english_link = preg_replace('/ /', '_', $t);
}
}
}
}
}
logger::log("WIKIPEDIA", "Language Scan Complete for ".$page);
logger::log("WIKIPEDIA", "User Link is ".$user_link." and english link is ".$english_link);
if ($user_link !== null) {
$format_domain = $domain;
$userdomain = true;
$page = $user_link;
$site = $domain.'.wikipedia.org';
} else if ($english_link !== null) {
$page = $english_link;
$site = "en.wikipedia.org";
$format_domain = "en";
}
}
if ($mobile) {
$req = 'http://'.$site.'/w/api.php?action=mobileview&sections=all&prop=text&page='.$page.'&format=xml';
} else {
$req = 'http://'.$site.'/w/api.php?action=parse&prop=text&page='.$page.'&format=xml';
}
$xml = wikipedia_request($req);
if ($xml !== null) {
$info = "";
if ($mobile) {
$info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$reformat = '<?xml version="1.0" encoding="UTF-8"?><api><parse><text xml:space="preserve">';
foreach($info->mobileview->sections->section as $section) {
$reformat .= htmlspecialchars($section, ENT_QUOTES);
}
$reformat .= '</text></parse><rompr><domain>'.$format_domain.'</domain><page>'.$page.'</page></rompr></api>';
return $reformat;
} else {
$info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$html = $info->parse->text;
$matches = array();
if (preg_match( '/REDIRECT <a href="\/wiki\/(.*?)"/', $html, $matches )) {
$xml = wikipedia_request('http://'.$format_domain.'.wikipedia.org/w/api.php?action=parse&prop=text&page='.$matches[1].'&format=xml');
$info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
} else if (preg_match( '/<ul class="redirectText"><li><a href=\"(.*?)\/w\/index.php\?title=(.*?)(\&.+)*\"/', $html, $matches)) {
logger::log("WIKIPEDIA", "Getting redirect page for ".$matches[2]." from ".$matches[1]);
// Wierd. $matches[1] always == "". WTF?
$xml = wikipedia_request('http://'.$format_domain.'.wikipedia.org/w/api.php?action=parse&prop=text&page='.$matches[2].'&format=xml');
$info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
}
return wrap_response($info, $format_domain, $page);
}
} else {
return "";
}
}
function wrap_response($xml, $domain, $page) {
$meta = $xml->addChild('rompr');
$meta->addChild('domain', $domain);
$meta->addChild('page', $page);
return $xml->asXML();
}
function join_responses($bits) {
$t = "";
$d = "";
$p = "";
foreach ($bits as $b) {
$info = simplexml_load_string($b, 'SimpleXMLElement', LIBXML_NOCDATA);
$t .= htmlspecialchars($info->parse->text, ENT_QUOTES);
$d = $info->rompr->domain;
$p = $info->rompr->page;
}
$reformat = '<?xml version="1.0" encoding="UTF-8"?><api><parse><text xml:space="preserve">'.$t.'</text></parse><rompr><domain>'.$d.'</domain><page>'.$p.'</page></rompr></api>';
return $reformat;
}
function send_result($xml) {
header('Content-Type: text/xml');
print $xml;
}
function send_failure($term) {
$xml = '<?xml version="1.0" encoding="UTF-8"?><api><parse><text xml:space="preserve">';
$xml .= htmlspecialchars('<h3 align="center">', ENT_QUOTES).get_int_text("wiki_fail", array($term)).htmlspecialchars('</h3>', ENT_QUOTES);
$xml .= '</text></parse>';
$xml .= '<rompr><domain>null</domain><page>null</page></rompr></api>';
send_result($xml);
}
// ==========================================================================
//
// Utility Functions
//
// ==========================================================================
function prepare_string($searchstring) {
// Escape naughty characters
$searchstring = preg_replace( '/(\(|\)|\^|\$|\\\\|\/)/', '\\\\$1', $searchstring );
return $searchstring;
}
function wikipedia_find_exact($searchfor, $domain) {
$xml = wikipedia_request('http://'.$domain.'.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($searchfor) . '&srprop=score&format=xml');
if ($xml == null) {
return '';
}
$info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$page = null;
// This is international, so we only look for an exact match (we can't possibly translate every possibility that's in artist_search, etc)
foreach ($info->query->search->p as $id) {
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $searchfor)) {
$page = $id['title'];
break;
}
}
if ($page == null) {
return '';
} else {
return get_wikipedia_page(preg_replace('/ /', '_', $page), $domain.".wikipedia.org", false);
}
}
function find_dismbiguation_page($page) {
$searchfor = $page.' (disambiguation)';
logger::log("WIKIPEDIA", "Searching Wikipedia for ".$searchfor);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($searchfor) . '&srprop=score&format=xml');
$results = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($results->query->search->p as $id) {
if ($id['title'] == $searchfor) {
logger::log("WIKIPEDIA", "returning disambiguation page for ".$page);
return get_wikipedia_page(preg_replace('/ /', '_', $id['title']), "en.wikipedia.org", true);
}
}
return '';
}
function wikipedia_get_list_of_suggestions($term) {
global $domain;
logger::log("WIKIPEDIA", "Getting list of suggestions for ".$term." from ".$domain.".wikipedia.org");
$xml = wikipedia_request('http://'.$domain.'.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($term) . '&srprop=score&format=xml');
if ($xml != "") {
$html = '<?xml version="1.0" encoding="UTF-8"?><api><parse><text xml:space="preserve">';
$xml = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
if (count($xml->query->search->p) == 0) {
return null;
}
$html .= htmlspecialchars('<h3 align="center">', ENT_QUOTES).get_int_text("wiki_suggest", array($term)).htmlspecialchars('</h3>', ENT_QUOTES);
$html .= htmlspecialchars('<h3 align="center">', ENT_QUOTES).get_int_text("wiki_suggest2").htmlspecialchars('</h3>', ENT_QUOTES);
$html .= htmlspecialchars('<ul>', ENT_QUOTES);
foreach ($xml->query->search->p as $id) {
$link = preg_replace('/\s/', '_', $id['title']);
$html .= htmlspecialchars('<li><a href="#" name="', ENT_QUOTES).$domain.'/'.htmlspecialchars($link, ENT_QUOTES).htmlspecialchars('" class="infoclick clickwikilink">'.$id['title'].'</a></li>', ENT_QUOTES);
}
$html .= htmlspecialchars("</ul>", ENT_QUOTES);
$html .= '</text></parse>';
$html .= '<rompr><domain>'.$domain.'</domain><page>'.htmlspecialchars($term, ENT_QUOTES).'</page></rompr></api>';
return $html;
} else {
return "";
}
}
// ==========================================================================
//
// Artist Search
//
// ==========================================================================
function getArtistWiki($artist_name, $disambig) {
global $domain;
// First, try a search and exact match in the user's chosen language.
// This is to catch the case where a page exists on that user's wikipedia
// domain and it has no language links to the en site
if ($domain != "en") {
$h = wikipedia_find_exact($artist_name, $domain);
if ($h != '') {
return $h;
}
}
// Now try a search on the english site. We can be more wide-ranging in this search
// we do this in English because (a) it has the most stuff and (b) I can speak it.
// We can find translation links later.
$h = wikipedia_artist_search($artist_name, $disambig);
if ($h != '') {
return $h;
}
// No results returned. If there's an '&' or 'and' or '+' in the name - such as 'Fruitbat & Umbrella'
// try querying for 'Fruitbat' and 'Umbrella' separately and if there are any results, display them all
$artist = preg_replace('/ and /', ' & ', $artist_name);
$artist = preg_replace('/\+/', '&', $artist);
$jhtml = array();
if (preg_match('/ & /', $artist) > 0) {
$alist = explode(' & ', $artist);
foreach ($alist as $artistname) {
$j = wikipedia_artist_search($artistname, "");
if ($j != '') {
$jhtml[] = $j;
}
}
} elseif (preg_match('/,/', $artist) > 0) {
$alist = explode(',', $artist);
$jhtml = array();
foreach ($alist as $artistname) {
$j = wikipedia_artist_search($artistname, "");
if ($j != '') {
$jhtml[] = $j;
}
}
}
if (count($jhtml) > 0) {
return join_responses($jhtml);
}
$h = find_dismbiguation_page($artist_name);
if ($h != '') {
return $h;
}
return wikipedia_get_list_of_suggestions($artist_name);
}
function wikipedia_artist_search($artist, $disambig) {
$page = null;
if ($disambig != "") {
$searchfor = $artist.' ('.$disambig.')';
logger::log("WIKIPEDIA ARTIST", "Searching Wikipedia for ".$searchfor);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($searchfor) . '&srprop=score&format=xml');
$artistinfo = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
// First look for exact match
foreach ($artistinfo->query->search->p as $id) {
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $searchfor)) {
$page = $id['title'];
break;
}
}
if ($page == null) {
$poss = array();
foreach ($artistinfo->query->search->p as $id) {
if (preg_match('/\(.*?band\)|\(.*?musician\)|\(.*?singer\)/i', $id['title'])) {
$poss[] = $id['title'];
}
}
if (count($poss) == 1) {
$page = array_shift($poss);
}
}
}
if ($page == null) {
logger::log("WIKIPEDIA ARTIST", "Searching Wikipedia for ".$artist);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($artist) . '&srprop=score&format=xml');
$artist2info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($artist2info->query->search->p as $id) {
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist)) {
$page = $id['title'];
break;
}
$poss = array();
if (preg_match('/\(.*?band\)|\(.*?musician\)|\(.*?singer\)/i', $id['title'])) {
$poss[] = $id['title'];
}
if (count($poss) == 1) {
$page = array_shift($poss);
break;
}
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', "The " . $artist)) {
$page = $id['title'];
break;
}
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*The ' . $searchstring . '\s*$/i', $artist)) {
$page = $id['title'];
break;
}
if (preg_match('/&/', $id['title'])) {
$searchstring = $id['title'];
$searchstring = preg_replace( '/&/', 'and', $searchstring );
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist)) {
$page = $id['title'];
break;
}
}
if (preg_match('/and/', $id['title'])) {
$searchstring = $id['title'];
$searchstring = preg_replace( '/and/', '&', $searchstring );
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist)) {
$page = $id['title'];
break;
}
}
// Any '.'? Let's remove them (both ways round)
if (preg_match('/\./', $id['title'])) {
$searchstring = $id['title'];
$searchstring = preg_replace( '/\./', '', $searchstring );
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist)) {
$page = $id['title'];
break;
}
}
if (preg_match('/\./', $artist)) {
$searchstring = $id['title'];
$t = preg_replace( '/\./', '', $artist );
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $t)) {
$page = $id['title'];
break;
}
}
// Words for numbers, numbers for words.
$numbers = array('/1/','/2/','/3/','/4/','/5/','/6/','/7/','/8/','/9/');
$words = array("one", "two", "three", "four", "five", "six", "seven", "eight", "nine");
$searchstring = $id['title'];
$searchstring = preg_replace( $numbers, $words, $searchstring);
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist) ||
preg_match('/^\s*' . $searchstring . '\s*$/i', "The ".$artist)) {
$page = $id['title'];
break;
}
$numbers = array('1','2','3','4','5','6','7','8','9');
$words = array("/one/", "/two/", "/three/", "/four/", "/five/", "/six/", "/seven/", "/eight/", "/nine/");
$searchstring = $id['title'];
$searchstring = preg_replace( $words, $numbers, $searchstring);
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $artist) ||
preg_match('/^\s*' . $searchstring . '\s*$/i', "The ".$artist)) {
$page = $id['title'];
break;
}
}
}
if ($page == null && preg_match('/.*\(.*\).*/', $artist)) {
$sf = trim(preg_replace('/\(.*?\)/','',$artist));;
logger::log("WIKIPEDIA ARTIST", "Searching Wikipedia for ".$sf);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($sf) . '&srprop=score&format=xml');
$artist3info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($artist3info->query->search->p as $id) {
$searchstring = $id['title'];
$searchstring = prepare_string($searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $sf)) {
$page = $id['title'];
break;
}
}
}
if ($page == null) {
return '';
}
logger::log("WIKIPEDIA ARTIST", "Artist search found page ".$page);
return get_wikipedia_page(preg_replace('/ /', '_', $page), "en.wikipedia.org", true);
}
// ==========================================================================
//
// Album Search
//
// ==========================================================================
function getAlbumWiki($album_name, $artist_name) {
global $domain;
// First, try a search and exact match in the user's chosen language.
// This is to catch the case where a page exists on that user's wikipedia
// domain and it has no language links to the en site
if ($domain != "en") {
$h = wikipedia_find_exact($album_name, $domain);
if ($h != '') {
return $h;
}
}
// Now try a search on the english site. We can be more wide-ranging in this search
// we do this in English because (a) it has the most stuff and (b) I can speak it.
// We can find translation links later.
$h = wikipedia_album_search($album_name, $artist_name);
if ($h != '') {
return $h;
}
$h = find_dismbiguation_page($album_name);
if ($h != '') {
return $h;
}
return wikipedia_get_list_of_suggestions($album_name);
}
function wikipedia_album_search($album, $artist) {
$album = munge_album_name($album);
logger::log("WIKIPEDIA ALBUM", "Searching Wikipedia for ".$album." (album)");
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($album." (album)") . '&srprop=score&format=xml');
$albuminfo = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$page = null;
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($album).'\s+\('.prepare_string($artist).' album\)';
// logger::log("WIKIDEBUG", "1. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
if ($page == null) {
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($album).'\s+\(album\)';
// logger::log("WIKIDEBUG", "2. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($album).'\s+\(\d+ album\)';
// logger::log("WIKIDEBUG", "2. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($album);
// logger::log("WIKIDEBUG", "3. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
logger::log("WIKIPEDIA ALBUM", "Searching Wikipedia for ".$album);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($album) . '&srprop=score&format=xml');
$album2info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($album2info->query->search->p as $id) {
$searchstring = prepare_string($album);
// logger::log("WIKIDEBUG", "3. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
return null;
}
logger::log("WIKIPEDIA ALBUM", "Album search found page ".$page);
return get_wikipedia_page(preg_replace('/ /', '_', $page), "en.wikipedia.org", true);
}
// ==========================================================================
//
// Track Search
//
// ==========================================================================
function getTrackWiki($track_name, $artist_name) {
global $domain;
// First, try a search and exact match in the user's chosen language.
// This is to catch the case where a page exists on that user's wikipedia
// domain and it has no language links to the en site
if ($domain != "en") {
$h = wikipedia_find_exact($track_name, $domain);
if ($h != '') {
return $h;
}
}
// Now try a search on the english site. We can be more wide-ranging in this search
// we do this in English because (a) it has the most stuff and (b) I can speak it.
// We can find translation links later.
$h = wikipedia_track_search($track_name, $artist_name);
if ($h != '') {
return $h;
}
$h = find_dismbiguation_page($track_name);
if ($h != '') {
return $h;
}
return wikipedia_get_list_of_suggestions($track_name);
}
function wikipedia_track_search($track, $trackartist) {
logger::log("WIKIPEDIA TRACK", "Searching Wikipedia for ".$track." (song) by ".$trackartist);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($track." (song)") . '&srprop=score&format=xml');
$albuminfo = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
// Comments assume the following:
// track is 'A Track'
// artist is 'An Artist'
$page = null;
// Look for 'A Track (An Artist song)'
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($track).'\s+\('.prepare_string($trackartist).' song\)';
// logger::log("WIKIDEBUG", "1. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
// Look for 'A Track (song)'
if ($page == null) {
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($track).'\s+\(song\)';
// logger::log("WIKIDEBUG", "2. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
// Look for 'A Track'
if ($page == null) {
foreach ($albuminfo->query->search->p as $id) {
$searchstring = prepare_string($track);
// logger::log("WIKIDEBUG", "3. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
logger::log("WIKIPEDIA TRACK", "Searching Wikipedia for ".$track);
$xml = wikipedia_request('http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' . rawurlencode($track) . '&srprop=score&format=xml');
$album2info = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
foreach ($album2info->query->search->p as $id) {
$searchstring = prepare_string($track);
// logger::log("WIKIDEBUG", "3. Checking page ".$id['title']." against ".$searchstring);
if (preg_match('/^\s*' . $searchstring . '\s*$/i', $id['title'])) {
logger::log("WIKIPEDIA", "Found Page : ".$id['title']);
$page = $id['title'];
break;
}
}
}
if ($page == null) {
return null;
}
logger::log("WIKIPEDIA TRACK", "Track search found page ".$page);
return get_wikipedia_page(preg_replace('/ /', '_', $page), "en.wikipedia.org", true);
}
?>

View File

@ -0,0 +1,157 @@
var discogs = function() {
var baseURL = 'https://api.discogs.com/';
var queue = new Array();
var throttle = null;
return {
request: function(reqid, data, success, fail) {
queue.push( {flag: false, reqid: reqid, data: data, success: success, fail: fail } );
debug.debug("DISCOGS","New request",data.url,"throttle is",throttle,"length is",queue.length);
if (throttle == null && queue.length == 1) {
discogs.getrequest();
}
},
getrequest: function() {
var req = queue[0];
clearTimeout(throttle);
if (req !== undefined) {
if (req.flag) {
debug.error("DISCOGS","Request just pulled from queue is already being handled",req.data.url);
return;
}
queue[0].flag = true;
debug.debug("DISCOGS","Taking next request from queue",req.data);
var getit = $.ajax({
method: 'POST',
url: "browser/backends/getdidata.php",
data: req.data,
dataType: "json"
})
.done(function(data) {
var c = getit.getResponseHeader('Pragma');
debug.debug("DISCOGS", "Request Success",c,data);
if (c == "From Cache") {
throttle = setTimeout(discogs.getrequest, 100);
} else {
throttle = setTimeout(discogs.getrequest, 1500);
}
req = queue.shift();
if (data === null) {
data = {error: language.gettext("discogs_error")}
} else if (!data.error) {
// info_discogs.js was written to accept jsonp data passed back from $.jsonp
// However as Discogs now seem to be refusing to respond to those requests
// we're using a php script to get it instead. So here we bodge the response
// into the form that info_discogs.js is expecting.
data = {data: data};
}
if (req.reqid != '') {
data.id = req.reqid;
}
if (data.error) {
req.fail(data);
} else {
req.success(data);
}
})
.fail(function(xhr,status,err) {
throttle = setTimeout(discogs.getrequest, 1500);
req = queue.shift();
debug.warn("DISCOGS","Request failed",req,xhr);
data = {error: language.gettext("discogs_error") + ' ('+xhr.status+' '+err+')'};
if (req.reqid != '') {
data.id = req.reqid;
}
req.fail(data);
});
} else {
throttle = null;
}
},
artist: {
search: function(name, success, fail) {
var data = {
url: baseURL+'database/search',
type: 'artist',
q: name
}
discogs.request('', data, success, fail);
},
getInfo: function(reqid, id, success, fail) {
var data = {url: baseURL+'artists/'+id};
discogs.request(reqid, data, success, fail);
},
getReleases: function(name, page, reqid, success, fail) {
debug.log("DISCOGS","Get Artist Releases",name,page);
var data = {
url: baseURL+'artists/'+name+'/releases',
per_page: 25,
page: page
};
discogs.request(reqid, data, success, fail);
}
},
album: {
getInfo: function(reqid, id, success, fail) {
// NOTE id must be either releases/id or masters/id
var data = {url: baseURL+id};
discogs.request(reqid, data, success, fail);
},
search: function(artist, album, success, fail) {
var data = {
url: baseURL+'database/search',
type: 'release',
artist: artist,
release_title: album
};
discogs.request('', data, success, fail);
}
},
track: {
getInfo: function(reqid, id, success, fail) {
// NOTE id must be either releases/id or masters/id
var data = {url: baseURL+id};
discogs.request(reqid, data, success, fail);
},
search: function(artist, track, success, fail) {
var data = {
url: baseURL+'database/search',
type: 'release',
artist: artist,
track: track
};
discogs.request('', data, success, fail);
}
},
label: {
getInfo: function(reqid, id, success, fail) {
// NOTE id must be either releases/id or masters/id
var data = {url: baseURL+'labels/'+id};
discogs.request(reqid, data, success, fail);
}
}
}
}();

View File

@ -0,0 +1,199 @@
var musicbrainz = function() {
var baseURL = 'http://musicbrainz.org/ws/2/';
var coverURL = 'http://coverartarchive.org/release/';
var queue = new Array();
var throttle = null;
return {
request: function(reqid, data, success, fail) {
queue.push( {flag: false, reqid: reqid, data: data, success: success, fail: fail } );
debug.debug("MUSICBRAINZ","New request",data.url);
if (throttle == null && queue.length == 1) {
musicbrainz.getrequest();
}
},
getrequest: function() {
var req = queue[0];
clearTimeout(throttle);
if (req) {
if (req.flag) {
debug.error("MUSICBRAINZ","Request just pulled from queue is already being handled");
return;
}
queue[0].flag = true;
debug.debug("MUSICBRAINZ","Taking next request from queue",req.data);
var getit = $.ajax({
method: 'POST',
url: "browser/backends/getmbdata.php",
data: req.data,
dataType: "json",
})
.done(function(data) {
var c = getit.getResponseHeader('Pragma');
debug.debug("MUSICBRAINZ","Request success",c,data);
if (c == "From Cache") {
throttle = setTimeout(musicbrainz.getrequest, 100);
} else {
throttle = setTimeout(musicbrainz.getrequest, 1500);
}
req = queue.shift();
if (data === null) {
data = {error: language.gettext("musicbrainz_error")};
}
if (req.reqid != '') {
data.id = req.reqid;
}
if (data.error) {
req.fail(data);
} else {
req.success(data);
}
})
.fail(function(xhr,status,err) {
throttle = setTimeout(musicbrainz.getrequest, 1500);
req = queue.shift();
debug.warn("MUSICBRAINZ","Request failed",req,xhr);
data = {error: language.gettext("musicbrainz_noinfo") + ' ('+xhr.status+' '+err+')'};
if (req.reqid != '') {
data.id = req.reqid;
}
req.fail(data);
});
} else {
throttle = null;
}
},
artist: {
getInfo: function(mbid, success, fail) {
var data = {
url: baseURL+'artist/'+mbid,
inc: 'aliases+tags+ratings+release-groups+artist-rels+label-rels+url-rels+release-group-rels+annotation',
fmt: 'json'
};
musicbrainz.request('', data, success, fail);
},
getReleases: function(mbid, reqid, success, fail) {
var result = { id: reqid };
result['release-groups'] = new Array();
(function getAllReleaseGroups() {
var data = {
url: baseURL+'release-group',
artist: mbid,
limit: 100,
fmt: 'json',
inc: 'artist-credits+tags+ratings+url-rels+annotation',
offset: result['release-groups'].length
};
musicbrainz.request(reqid, data, function(data) {
debug.debug("MUSICBRAINZ","Release group data:",data);
if (data.error) {
if (result['release-groups'].length > 0) {
success(result);
} else {
fail(data);
}
} else {
for (var i in data['release-groups']) {
result['release-groups'].push(data['release-groups'][i]);
}
if (result['release-groups'].length == data['release-group-count']) {
success(result);
} else {
getAllReleaseGroups();
}
}
}, fail);
})();
}
},
album: {
getInfo: function(mbid, success, fail) {
var data = {
url: baseURL+'release/'+mbid,
inc: 'annotation+tags+ratings+artists+labels+recordings+release-groups+artist-credits+url-rels+release-group-rels+recording-rels+artist-rels',
fmt: 'json'
};
musicbrainz.request('', data, success, fail);
},
getCoverArt: function(id, success, fail) {
var data = {url: coverURL + id + "/" };
musicbrainz.request('', data, success, fail);
},
},
releasegroup: {
getInfo: function(mbid, reqid, success, fail) {
var data = {
url: baseURL+'release-group/'+mbid,
inc: 'artists+releases+artist-rels+label-rels+url-rels',
fmt: 'json'
};
musicbrainz.request(reqid, data, success, fail);
}
},
track: {
getInfo: function(mbid, success, fail) {
var data = {
url: baseURL+'recording/'+mbid,
inc: 'annotation+tags+ratings+releases+url-rels+work-rels+release-rels+release-group-rels+artist-rels+label-rels+recording-rels',
fmt: 'json'
};
var result = {};
// For a track, although there might be some good stuff in the recording data, what we really want
// is the associated work, if there is one, because that's where the wiki and discogs links will probably be.
musicbrainz.request('', data,
function(data) {
result.recording = data;
debug.debug("MUSICBRAINZ","Scanning recording for work data");
for (var i in data.relations) {
if (data.relations[i].work) {
debug.debug("MUSICBRAINZ","Found work data",data.relations[i].work.id);
var newdata = {
url: baseURL+'work/'+data.relations[i].work.id,
inc: 'annotation+tags+ratings+url-rels+artist-rels',
fmt: 'json'
};
musicbrainz.request('', newdata,
function(workdata) {
debug.debug("MUSICBRAINZ","Got work data",workdata);
result.work = workdata;
success(result);
},
function(workdata) {
debug.debug("MUSICBRAINZ","Got NO work data",workdata);
success(result);
});
return;
}
}
success(result);
},
fail);
}
}
}
}();

View File

@ -0,0 +1,40 @@
var soundcloud = function() {
var self = this;
return {
getTrackInfo: function(mopidyURI, callback) {
// "soundcloud:song/King Tubby meets Soul Rebel Uptown.92868852"
debug.log("SOUNDCLOUD","Trying to get track info from",mopidyURI);
var a = mopidyURI.match(/(\d+)$/);
var tracknum = a[1];
debug.log("SOUNDCLOUD","Getting soundcloud info for track",tracknum);
$.ajax({
method: 'POST',
dataType: 'json',
url: 'browser/backends/getscdata.php',
data: {url: 'tracks/'+tracknum+'.json'}
})
.done(callback)
.fail(function(xhr,status,err) {
debug.warn("SOUNDCLOUD","SoundCloud Error",xhr);
callback(xhr.responseJSON);
});
},
getUserInfo: function(userid, callback) {
debug.log("SOUNDCLOUD","Getting soundcloud info for user",userid);
$.ajax({
method: 'POST',
dataType: 'json',
url: 'browser/backends/getscdata.php',
data: {url: 'users/'+userid+'.json'}
})
.done(callback)
.fail(function(xhr,status,err) {
debug.warn("SOUNDCLOUD","SoundCloud Error",xhr);
callback(xhr.responseJSON);
});
}
}
}();

View File

@ -0,0 +1,216 @@
var spotify = function() {
var baseURL = 'https://api.spotify.com';
var queue = new Array();
var throttle = null;
var collectedobj = null;
var getit;
var rate = 500;
var backofftimer;
function objFirst(obj) {
for (var a in obj) {
return a;
}
}
return {
request: function(reqid, url, success, fail, prio, cache) {
if (prio && queue.length > 1) {
queue.splice(1, 0, {flag: false, reqid: reqid, url: url, success: success, fail: fail, cache: cache } );
} else {
queue.push( {flag: false, reqid: reqid, url: url, success: success, fail: fail, cache: cache } );
}
debug.debug("SPOTIFY","New request",url,throttle,queue.length,cache);
if (throttle == null && queue.length == 1) {
spotify.getrequest();
}
},
requestSuccess: function(data) {
var c = getit.getResponseHeader('Pragma');
debug.debug("SPOTIFY","Request success",c,data);
req = queue.shift();
if (data === null) {
debug.warn("SPOTIFY","No data in response",req);
data = {error: language.gettext("spotify_error")};
}
if (req.reqid != '') {
data.reqid = req.reqid;
}
var root = objFirst(data);
if (data[root].next) {
debug.log("SPOTIFY","Got a response with a next page!");
if (data[root].previous == null) {
collectedobj = data;
} else {
collectedobj[root].items = collectedobj[root].items.concat(data[root].items);
}
queue.unshift({flag: false, reqid: '', url: data[root].next, success: req.success, fail: req.fail});
} else if (data[root].previous) {
collectedobj[root].items = collectedobj[root].items.concat(data[root].items);
debug.log("SPOTIFY","Returning concatenated multi-page result");
req.success(collectedobj);
} else if (data.next) {
debug.log("SPOTIFY","Got a response with a next page!");
if (data.previous == null) {
collectedobj = data;
} else {
collectedobj.items = collectedobj.items.concat(data.items);
}
queue.unshift({flag: false, reqid: '', url: data.next, success: req.success, fail: req.fail});
} else if (data.previous) {
collectedobj.items = collectedobj.items.concat(data.items);
debug.log("SPOTIFY","Returning concatenated multi-page result");
req.success(collectedobj);
} else {
req.success(data);
}
if (c == "From Cache") {
throttle = setTimeout(spotify.getrequest, 100);
} else {
throttle = setTimeout(spotify.getrequest, rate);
}
},
requestFail: function(xhr,status,err) {
if (xhr.responseJSON.error == 429) {
debug.warn("SPOTIFY","Too Many Requests. Slowing Request Rate");
rate += rate;
clearTimeout(backofftimer);
backofftimer = setTimeout(spotify.speedBackUp, 90000);
}
throttle = setTimeout(spotify.getrequest, rate);
req = queue.shift();
debug.warn("SPOTIFY","Request failed",req,xhr,status,err);
data = {error: language.gettext("spotify_noinfo") + ' ('+xhr.responseJSON.error+' '+xhr.responseJSON.message+')'}
if (req.reqid != '') {
data.reqid = req.reqid;
}
req.fail(data);
},
speedBackUp: function() {
rate = 500;
},
getrequest: function() {
var req = queue[0];
clearTimeout(throttle);
if (req) {
if (req.flag) {
debug.warn("SPOTIFY","Request just pulled from queue is already being handled",req,throttle);
return;
}
queue[0].flag = true;
debug.debug("SPOTIFY","Taking next request from queue",req.url);
getit = $.ajax({
type: 'POST',
url: "browser/backends/getspdata.php",
dataType: "json",
data: {
url: req.url,
cache: req.cache
}
})
.done(spotify.requestSuccess)
.fail(spotify.requestFail);
} else {
throttle = null;
}
},
track: {
getInfo: function(id, success, fail, prio) {
var url = baseURL + '/v1/tracks/' + id;
spotify.request('', url, success, fail, prio, true);
},
checkLinking: function(id, success, fail, prio) {
var url = baseURL + '/v1/tracks/' + id + '?market='+prefs.lastfm_country_code;
spotify.request('', url, success, fail, prio, false);
}
},
tracks: {
checkLinking: function(ids, success, fail, prio) {
var url = baseURL + '/v1/tracks?ids='+ids.join(',')+'&market='+prefs.lastfm_country_code;
spotify.request('', url, success, fail, prio, false);
}
},
album: {
getInfo: function(id, success, fail, prio) {
var url = baseURL + '/v1/albums/' + id;
spotify.request(id, url, success, fail, prio, true);
},
getMultiInfo: function(ids, success, fail, prio) {
var url = baseURL + '/v1/albums/?ids=' + ids.join();
spotify.request('', url, success, fail, prio, true);
}
},
artist: {
getInfo: function(id, success, fail, prio) {
var url = baseURL + '/v1/artists/' + id;
spotify.request('', url, success, fail, prio, true);
},
getRelatedArtists: function(id, success, fail, prio) {
var url = baseURL + '/v1/artists/' + id + '/related-artists'
spotify.request('', url, success, fail, prio, true);
},
getTopTracks: function(id, success, fail, prio) {
var url = baseURL + '/v1/artists/' + id + '/top-tracks'
spotify.request('', url, success, fail, prio, true);
},
getAlbums: function(id, types, success, fail, prio) {
var url = baseURL + '/v1/artists/'+id+'/albums?album_type='+types+'&market='+prefs.lastfm_country_code+'&limit=50';
spotify.request(id, url, success, fail, prio, true);
},
search: function(name, success, fail, prio) {
var url = baseURL + '/v1/search?q='+name.replace(/&|%|@|:|\+|'|\\|\*|"|\?|\//g,'').replace(/\s+/g,'+')+'&type=artist';
spotify.request('', url, success, fail, prio, true);
}
},
recommendations: {
getGenreSeeds: function(success, fail) {
var url = baseURL + '/v1/recommendations/available-genre-seeds';
spotify.request('', url, success, fail, true, true);
},
getRecommendations: function(param, success, fail) {
var p = new Array();
for (var i in param) {
p.push(i+'='+encodeURIComponent(param[i]));
}
var paramstring = p.join('&');
var url = baseURL + '/v1/recommendations?'+paramstring;
spotify.request('', url, success, fail, false, false);
}
}
}
}();

View File

@ -0,0 +1,77 @@
var wikipedia = function() {
return {
getLanguage: function() {
if (lastfm.getLanguage()) {
return lastfm.getLanguage();
} else {
return "en";
}
},
search: function(terms, successCallback, failCallback) {
terms.lang = wikipedia.getLanguage();
terms.layout = skin;
$.ajax({
type: "POST",
url: "browser/backends/info_wikipedia.php",
data: terms,
dataType: "xml"
})
.done(successCallback)
.fail(failCallback);
},
getFullUri: function(terms, successCallback, failCallback) {
terms.lang = wikipedia.getLanguage();
terms.layout = skin;
$.ajax({
type: "POST",
url: "browser/backends/info_wikipedia.php",
data: terms,
dataType: "xml"
})
.done(successCallback)
.fail(failCallback);
},
wikiMediaPopup: function(element, event) {
var thing = element.attr("name");
debug.log("WIKIPEDIA","Clicked element has name",thing);
var a = thing.match(/(.*?)\/(.*)/);
if (a && a[1] && a[2]) {
var fname = a[2];
if (fname.match(/jpg$/i) || fname.match(/gif$/i) || fname.match(/png$/i) || fname.match(/jpeg$/i) || fname.match(/svg$/i) || fname.match(/bmp$/i)) {
imagePopup.create(element, event);
var url = "http://"+a[1]+"/w/api.php?action=query&iiprop=url|size&prop=imageinfo&titles=" + a[2] + "&format=json&callback=?";
$.getJSON(url, function(data) {
$.each(data.query.pages, function(index, value) {
imagePopup.create(element, event, 'getRemoteImage.php?url='+value.imageinfo[0].url);
return false;
});
}).fail( function() { imagePopup.close() });
}
}
return false;
},
getWiki: function(link, successCallback, failCallback) {
$("#infopane").css({cursor:'wait'});
$("#infopane a").css({cursor:'wait'});
$.ajax({
type: "POST",
url: "browser/backends/info_wikipedia.php",
data: {wiki: link, layout: skin},
dataType: "xml"
})
.done(successCallback)
.fail(failCallback)
.always(function() {
$("#infopane").css({cursor:'auto'});
$("#infopane a").css({cursor:'auto'});
});
},
}
}();

573
www/jukebox/browser/info.js Normal file
View File

@ -0,0 +1,573 @@
var browser = function() {
var history = [{
source: "",
artist: {
name: "",
},
album: {
name: "",
artist: "",
},
track: {
name: "",
}
}];
var displaypointer = 0;
var panelclosed = {artist: false, album: false, track: false};
var waitingon = {artist: false, album: false, track: false, index: -1, source: null};
var extraPlugins = [];
var maxhistorylength = 20;
var sources = nowplaying.getAllPlugins();
var doneone = false;
function displayTheData(ptr, showartist, showalbum, showtrack) {
var a = waitingon.artist;
var b = waitingon.album;
var c = waitingon.track;
waitingon = { artist: a || showartist,
album: b || showalbum,
track: c || showtrack,
index: history[ptr].mastercollection.nowplayingindex,
source: history[ptr].source
};
debug.log("BROWSER", "Waiting on source",waitingon.source,"for index", waitingon.index);
if (waitingon.source != prefs.infosource) {
// Need to do this here rather than in switchsource otherwise it prevents
// the browser from accepting the update
$("#button_source"+prefs.infosource).removeClass("currentbun");
prefs.save({infosource: waitingon.source});
debug.log("BROWSER", "Source switched to",prefs.infosource);
$("#button_source"+prefs.infosource).addClass("currentbun");
}
for (var i in waitingon) {
if (waitingon[i] === true) {
$("#"+i+"information").html(waitingBanner(i));
}
}
for (var i = 1; i < history.length-1; i++) {
history[i].mastercollection.stopDisplaying();
}
// Remember, here we tell artist, album, and track to display even if we only want one of them.
// This is because we need the new collections to handle clicks and other stuff,
// as otherwise it all gets very out of hand and impossible to follow,
// mainly because it's super tricky to keep the stopDisplaying/displayData displaying flags
// all in sync since they're global to one dataCollection and not individual for artist,
// album, and track. (This was tried before and got stupid).
history[ptr].mastercollection.sendDataToBrowser(waitingon);
if (displaypointer == history.length-1) {
// We only allow artist switching on the current playing track.
// It's not that it doesn't work, but it means the artist switch gets added to
// the end of history and then you have to go back to get to the current track,
// which means things stop auto-updating.
// Also it makes truncating the history really hard.
// TODO perhaps artist switches should be spliced in?
history[ptr].mastercollection.doArtistChoices();
} else {
if ($("#artistchooser").is(':visible')) {
$("#artistchooser").slideUp('fast');
}
}
}
function waitingBanner(which) {
var html = '<div class="containerbox infosection menuitem">';
html += '<h2 class="expand"><span class="ucfirst">'+language.gettext("label_"+which)+
'</span> : '+language.gettext("info_gettinginfo")+'</h2>';
html += '<div class="fixed alignmid"><i class="icon-spin6 svg-square spinner"></i></div>';
html += '</div>';
return html;
}
function banner(data, title, hidden, source, close) {
var html = '<div class="containerbox infosection menuitem">';
if (source) {
html += '<h2 class="expand"><span class="ucfirst">'+
language.gettext("label_"+title)+'</span> : ' + data.name + '</h2>';
} else {
html += '<h2 class="expand">' + data.name + '</h2>';
}
html += '<div class="fixed alignmid">';
html += '<i class="icon-menu svg-square infoclick clickicon frog tooltip" title="'+language.gettext('label_hidepanel')+'"></i>';
html += '</div>';
if (data.help) {
html += '<div class="fixed alignmid"><a href="'+data.help+'" target="_blank">'+
'<i class="icon-info-circled svg-square tooltip" title="'+language.gettext('label_gethelp')+'"></i></a></div>';
}
if (source) {
if (data.link) {
html += '<div class="fixed alignmid"><a href="'+data.link+'" target="_blank">'+
'<i class="'+sources[source].icon+' svg-square tooltip" title="'+language.gettext("info_newtab")+'"></i></a></div>';
} else {
html += '<div class="fixed alignmid"><i class="'+sources[source].icon+' svg-square"></i></div>';
}
}
if (close) {
html += '<div class="fixed alignmid padright"><i class="icon-cancel-circled svg-square infoclick clickicon tadpole tooltip" title="'+language.gettext('label_closepanel')+'"></i></div>';
}
html += '</div>';
html += '<div class="foldup" id="'+title+'foldup"';
if (hidden) {
html += ' style="display:none"';
}
html += '>';
return html;
}
function toggleSection(element) {
var foldup = element.parent().parent().next();
var section = element.parent().parent().parent().attr("id");
$(foldup).slideToggle('slow', function() {
if ($(this).is(':visible')) {
browser.rePoint();
}
});
section = section.replace(/information/,'');
panelclosed[section] = !panelclosed[section];
}
function updateHistory() {
$('#historypanel').off('click').empty().html('<div class="configtitle textcentre"><b>'
+language.gettext("button_history")
+'</b><i class="icon-cancel-circled clickicon playlisticonr tright mobonly" onclick="showHistory()"></i></div>'
);
if (displaypointer == 1) {
$("#backbutton").off('click').addClass('button-disabled');
}
if (displaypointer > 1 && $("#backbutton").hasClass('button-disabled')) {
$("#backbutton").on('click', browser.back );
$("#backbutton").removeClass('button-disabled');
}
if (displaypointer == (history.length)-1) {
$("#forwardbutton").off('click').addClass('button-disabled');
}
if (displaypointer < (history.length)-1 && $("#forwardbutton").hasClass('button-disabled')) {
$("#forwardbutton").on('click', browser.forward );
$("#forwardbutton").removeClass('button-disabled');
}
var bits = ["artist","album","track"];
var t = $('<table>', {class: 'histable', width: '100%'}).appendTo('#historypanel');
for (var i = 1; i < history.length; i++) {
var clas = "top clickable clickicon";
if (i == displaypointer) {
clas = clas + " current";
}
var r = $('<tr>', {class: clas, name: i}).appendTo(t);
r.append('<td><i class="'+sources[history[i].source].icon+' medicon"></i></td>');
var td = $('<td>').appendTo(r);
var html = '';
bits.forEach(function(n) {
if (history[i][n].collection) {
html += history[i][n].collection.bannername()+'<br />';
} else {
html += language.gettext("label_"+n)+' : '+history[i][n].name+'<br>';
}
});
td.html(html);
}
$('#historypanel').on('click', browser.historyClicked);
}
function removeSection(section) {
extraPlugins[section].parent.close();
extraPlugins[section].div.fadeOut('fast', function() {
extraPlugins[section].div.empty();
extraPlugins[section].div.remove();
extraPlugins[section].div = null;
if ($('#pluginholder').length > 0 && openPlugins() == 0) {
layoutProcessor.sourceControl('specialplugins');
}
});
}
function openPlugins() {
var c = 0;
for (var i in extraPlugins) {
if (extraPlugins[i].div !== null) {
c++;
}
}
return c;
}
function checkHistoryLength() {
if (history.length > maxhistorylength) {
debug.shout("BROWSER", "Truncating History");
var np = history[1].mastercollection.nowplayingindex;
history.splice(1,1);
displaypointer--;
for (var i = 1; i < history.length; i++) {
// Scan our history to see if this nowplayingindex is being used anywhere else
if (history[i].mastercollection.nowplayingindex == np) {
return;
}
}
debug.log("BROWSER","Telling nowplaying to remove nowplayingindex",np);
nowplaying.remove(np);
}
}
return {
historyClicked: function(event) {
var clickedRow = $(event.target);
while (!clickedRow.hasClass('clickable') && !clickedRow.is('#historypanel')) {
clickedRow = clickedRow.parent();
}
if (clickedRow.hasAttr('name')) {
browser.doHistory(clickedRow.attr('name'));
}
},
areweatfront: function() {
debug.log("BROWSER","displaypointer:",displaypointer,"historylength",history.length);
return (displaypointer == history.length - 1);
},
createButtons: function() {
for (var i in sources) {
if (sources[i].icon !== null) {
debug.log("BROWSER", "Found plugin", i,sources[i].icon);
layoutProcessor.addInfoSource(i, sources[i]);
}
}
layoutProcessor.setupInfoButtons();
},
nextSource: function(direction) {
var s = new Array();
for (var i in sources) {
if (sources[i].icon !== null) {
s.push(i);
}
}
var cursourceidx = s.indexOf(prefs.infosource);
var newsourceidx = cursourceidx+direction;
if (newsourceidx >= s.length) newsourceidx = 0;
if (newsourceidx < 0) newsourceidx = s.length-1;
browser.switchsource(s[newsourceidx]);
},
dataIsComing: function(mastercollection, isartistswitch, nowplayingindex, source, trackartist, artist, albumartist, album, track) {
debug.log("BROWSER","Data is coming",isartistswitch, nowplayingindex, source, artist, albumartist, album, track)
if (prefs.hidebrowser) {
debug.log("BROWSER","Browser is hidden. Ignoring Data");
return;
}
var showalbum = (album != history[displaypointer].album.name || albumartist != history[displaypointer].album.artist || source != prefs.infosource);
var showartist = (isartistswitch || artist != history[displaypointer].artist.name || source != prefs.infosource ||
(showalbum && artist != history[displaypointer].artist.name));
var showtrack = (track != history[displaypointer].track.name || showalbum || source != prefs.infosource);
checkHistoryLength();
history.push( {
mastercollection: mastercollection,
source: source,
trackartist: trackartist,
artist: {
name: artist,
collection: null
},
album: {
name: album,
artist: albumartist,
collection: null
},
track: {
name: track,
collection: null
}
});
// Display the new data only if either:
// We are currently displaying the most recent track (ie continuous updates)
// This is an artist switch request
// This is a source switch request
// History has been truncated such that the currently displayed info needs to be removed
if (displaypointer == history.length - 2 || isartistswitch || source != prefs.infosource || displaypointer < 1) {
displaypointer = history.length - 1;
// Hack timeout to get around a problem where Masonry doesn't layout properly
// during page load.
if (doneone) {
displayTheData( displaypointer, showartist, showalbum, showtrack );
} else {
setTimeout(function() {
displayTheData( displaypointer, showartist, showalbum, showtrack );
doneone = true;
}, 1000);
}
}
updateHistory();
},
Update: function(collection, type, source, nowplayingindex, data, scrollto, force) {
if (prefs.hidebrowser) {
return false;
}
debug.mark("BROWSER", "Got",type,"info from",source,"for index",nowplayingindex,force,waitingon);
if (force === true || (source == waitingon.source && nowplayingindex == waitingon.index)) {
if (force === true || waitingon[type]) {
debug.trace("BROWSER", " .. and we are going to display it");
if (data.data !== null && (source == "file" || data.name !== "")) {
if ($("#"+type+"information").is(':hidden')) {
$("#"+type+"information").show();
}
$("#"+type+"information").html(banner(data, (collection === null) ? type : collection.bannertitle(), panelclosed[type], source)+data.data);
} else {
$("#"+type+"information").empty();
if ($("#"+type+"information").is(':visible')) {
$("#"+type+"information").hide();
}
}
waitingon[type] = false;
if (scrollto) {
layoutProcessor.goToBrowserPanel(type);
}
return true;
} else {
return false;
}
}
},
reDo: function(index, source) {
if (history[displaypointer].mastercollection && index == history[displaypointer].mastercollection.nowplayingindex && source == prefs.infosource) {
debug.log("BROWSER","Re-displaying data for",source,"index",index);
displayTheData(displaypointer, true, true, true);
}
},
switchsource: function(src) {
debug.log("BROWSER","Switching to",src);
if (displaypointer >= 1) {
displaypointer = history.length - 1;
history[displaypointer].mastercollection.populate(src, true);
updateHistory();
}
},
handleClick: function(source, element, event) {
debug.log("BROWSER","Was clicked on",source,element);
if (element.hasClass('frog')) {
toggleSection(element);
} else if (element.hasClass('tadpole')) {
removeSection(source);
} else if (element.hasClass('plugclickable')) {
extraPlugins[source].parent.handleClick(element, event);
} else if (element.hasClass('clickartistchoose')) {
nowplaying.switchArtist(history[displaypointer].source, element.next().val());
} else {
history[displaypointer].mastercollection.handleClick(history[displaypointer].source, source, element, event);
}
},
// This function is for links which are followed internally by one of the panels
// eg wikipedia
speciaUpdate: function(source, panel, data) {
debug.mark("BROWSER","Special Update from",source,"for",panel);
var n = new specialUpdateCollection(source, panel, data);
history.splice(displaypointer+1,0, {
mastercollection: history[displaypointer].mastercollection,
source: source,
trackartist: history[displaypointer].trackartist,
artist: {
name: history[displaypointer].artist.name,
collection: (panel == "artist") ? n : history[displaypointer].artist.collection
},
album: {
name: history[displaypointer].album.name,
albumartist: history[displaypointer].albumartist,
collection: (panel == "album") ? n : history[displaypointer].album.collection
},
track: {
name: history[displaypointer].track.name,
collection: (panel == "track") ? n : history[displaypointer].track.collection
}
});
waitingon[panel] = true;
waitingon.source = source;
waitingon.index = history[displaypointer].mastercollection.nowplayingindex;
displaypointer++;
updateHistory();
browser.Update(n, panel, source, waitingon.index, data, true);
},
doHistory: function(index) {
debug.log("BROWSER", "Doing history, index is",index);
var showartist = (history[index].artist.collection === null &&
(history[index].artist.name != history[displaypointer].artist.name ||
history[index].source != history[displaypointer].source ||
history[displaypointer].artist.collection !== null));
var showalbum = (history[index].album.collection === null &&
(history[index].album.name != history[displaypointer].album.name ||
history[index].album.artist != history[displaypointer].album.artist ||
history[index].source != history[displaypointer].source ||
history[displaypointer].album.collection !== null));
var showtrack = (history[index].track.collection === null &&
(history[index].track.name != history[displaypointer].track.name ||
history[index].album.name != history[displaypointer].album.name ||
history[index].album.artist != history[displaypointer].album.artist ||
history[index].source != history[displaypointer].source ||
history[displaypointer].track.collection !== null));
displaypointer = index;
debug.log("BROWSER","History flags are",showartist,showalbum,showtrack);
// Calling displayTheData is important even if all the showxxx flags are false
// since it makes sure the correct trackDataCollection gets its displaying flag set.
displayTheData(displaypointer, showartist, showalbum, showtrack);
updateHistory();
var bits = ["artist","album","track"];
bits.forEach(function(n) {
debug.log("BROWSER","Updating",n);
if (history[index][n].collection) {
waitingon[n] = true;
waitingon.source = history[index].source;
waitingon.index = history[index].mastercollection.nowplayingindex;
browser.Update(history[index][n].collection, n, waitingon.source, waitingon.index, history[index][n].collection.getData());
}
});
layoutProcessor.afterHistory();
},
forward: function() {
browser.doHistory(displaypointer+1);
return false;
},
back: function() {
browser.doHistory(displaypointer-1);
return false;
},
registerExtraPlugin: function(id, name, parent, help) {
var displayer;
if (prefs.hidebrowser) {
$("#hidebrowser").prop("checked", !$("#hidebrowser").is(':checked'));
prefs.save({hidebrowser: $("#hidebrowser").is(':checked')}, hideBrowser);
}
if ($('#pluginholder').length > 0 && !($('#pluginholder').is(':visible'))) {
displayer = $('<div>', {id: id+"information", class: "infotext invisible"}).appendTo('#pluginholder');
} else {
displayer = $('<div>', {id: id+"information", class: "infotext invisible"}).insertBefore('#artistchooser');
}
var opts = {name: name};
if (help) {
opts.help = help;
}
displayer.html(banner(opts, id, false, false, true));
panelclosed[id] = false;
displayer.off('click');
extraPlugins[id] = { div: displayer, parent: parent };
displayer.on('click', '.infoclick', onBrowserClicked);
return displayer;
},
goToPlugin: function(id) {
layoutProcessor.goToBrowserPlugin(id);
},
rePoint: function(panel, params) {
if (prefs.hidebrowser || $('#infopane').width() == 0) { return }
debug.debug("BROWSER","Repointing");
layoutProcessor.updateInfopaneScrollbars();
$('#infopane .masonified:visible').each(function() {
var h = $(this);
var width = calcPercentWidth(h, '.tagholder', 500, h.width());
h.find(".tagholder").css('width', width.toString()+'%');
// Check if it is being handled by Masonry, prevents early init with no paramerts
if (typeof(params) == 'undefined' && h.css('position') == 'relative') {
h.masonry();
}
});
$('#infopane .masonified5.filled:visible').each(function() {
var h = $(this);
var width = calcPercentWidth(h, '.tagholder5', 260, h.width());
h.find(".tagholder5").css('width', width.toString()+'%');
h.find(".sizer").css('width', width.toString()+'%');
if (typeof(params) == 'undefined' && h.css('position') == 'relative') {
h.masonry();
}
});
$('#infopane .masonified2:visible').each(function() {
var h = $(this);
var width = calcPercentWidth(h, '.tagholder2', 260, h.width());
h.find(".tagholder2").css('width', width.toString()+'%');
h.find(".sizer").css('width', width.toString()+'%');
h.find(".tagholder_wide").css("width", "100%");
h.find(".brick_wide").css("width", "100%");
if (typeof(params) == 'undefined' && h.css('position') == 'relative') {
h.masonry();
}
});
$('#infopane .masonified7:visible').each(function() {
var h = $(this);
if (h.width() > 800) {
var width = 48;
} else {
var width = 99;
}
h.find(".tagholder2").css('width', width.toString()+'%');
h.find(".tagholder_wode").css("width", "100%");
if (typeof(params) == 'undefined' && h.css('position') == 'relative') {
h.masonry();
}
});
$('#infopane .masonified4:visible').each(function() {
var h = $(this);
var width = calcPercentWidth(h, '.tagholder4', 140, h.width());
h.find(".tagholder4").css('width', width.toString()+'%');
if (typeof(params) == 'undefined' && h.css('position') == 'relative') {
h.masonry();
}
});
$('#infopane .mixcontainer:visible').each(function() {
var h = $(this);
var w = h.width();
var m = h.children('.mixbox');
if (m.length == 1 || w < 700) {
m.css('width', '100%');
} else {
m.css('width', '50%');
}
});
if (typeof(params) != 'undefined') {
params.gutter = masonry_gutter;
panel.masonry(params);
}
}
}
}();
function specialUpdateCollection(source, panel, data) {
this.bannertitle = function() {
return source;
}
this.bannername = function() {
return data.name;
}
this.getData = function() {
return data;
}
}

View File

@ -0,0 +1,310 @@
var info_file = function() {
var me = "file";
function podComment(parent) {
if (parent.playlistinfo.type == 'podcast' && parent.playlistinfo.Comment) {
return '<div class="brick tagholder_wode tagholder"><table class="fileinfotable" style="width:100%"><tr><th>'+
language.gettext("info_comment").replace(':','')+'</th></tr><tr><td class="notbold">'+parent.playlistinfo.Comment+'</td></tr></table></div>';
}
return '';
}
function createInfoFromPlayerInfo(info, parent) {
var html = "";
var file = decodeURI(info.file);
debug.log("FILE INFO","Decoded File Name is",file);
file = file.replace(/^file:\/\//, '');
var filetype = "";
if (file) {
var n = file.match(/.*\.(.*?)$/);
if (n) {
filetype = n[n.length-1];
filetype = filetype.toLowerCase();
if (filetype.match(/\/|\?|\=/)) {
filetype = "";
}
}
}
if (file == "null" || file == "undefined") file = "";
html += '<table class="fileinfotable" style="width:100%">';
html += '<tr><th colspan="2">Format Information</th></tr>';
html += '<tr><td>'+language.gettext("info_file")+'</td><td>'+file;
if (file.match(/^http:\/\/.*item\/\d+\/file/)) html += ' <i>'+language.gettext("info_from_beets")+'</i>';
if (info.file) {
var f = info.file.match(/^podcast[\:|\+](http.*?)\#/);
if (f && f[1]) {
html += '<button onclick="podcasts.doPodcast(\'filepodiput\')">'+language.gettext('button_subscribe')+'</button>'+
'<input type="hidden" id="filepodiput" value="'+f[1]+'" />';
}
}
html += '</td></tr>';
if (filetype != "" && !file.match(/^http/)) {
html += '<tr><td>'+language.gettext("info_format")+'</td><td>'+filetype+'</td></tr>';
}
if (info.bitrate && info.bitrate != 'None' && info.bitrate != 0) {
html += '<tr><td>'+language.gettext("info_bitrate")+'</td><td>'+info.bitrate+'</td></tr>';
}
var ai = info.audio;
if (ai) {
var p = ai.split(":");
html += '<tr><td>'+language.gettext("info_samplerate")+'</td><td>'+p[0]+' Hz, '+p[1]+' Bit, ';
if (p[2] == 1) {
html += language.gettext("info_mono");
} else if (p[2] == 2) {
html += language.gettext("info_stereo");
} else {
html += p[2]+' '+language.gettext("info_channels");
}
'</td></tr>';
}
if (info.Date) {
if (typeof info.Date == "string") {
info.Date = info.Date.split(';');
}
html += '<tr><td>'+language.gettext("info_date")+'</td><td>'+info.Date[0]+'</td></tr>';
}
if (info.Genre) {
if (typeof info.Genre == "string") {
info.Genre = info.Genre.split(';');
}
html += '<tr><td>'+language.gettext("info_genre")+'</td><td>'+info.Genre.join(', ')+'</td></tr>';
}
if (info.Performer) {
if (typeof info.Performer == "object") {
info.Performer = info.Performer.join(';');
}
html += '<tr><td>'+language.gettext("info_performers")+'</td><td>'+concatenate_artist_names(info.Performer.split(';'))+'</td></tr>';
}
if (info.Composer) {
if (typeof info.Composer == "object") {
info.Composer = info.Composer.join(';');
}
html += '<tr><td>'+language.gettext("info_composers")+'</td><td>'+concatenate_artist_names(info.Composer.split(';'))+'</td></tr>';
}
if (info.Comment) {
if (typeof info.Comment == "object") {
info.Comment = info.Comment.join('<br>');
}
html += '<tr><td>'+language.gettext("info_comment")+'</td><td>'+info.Comment+'</td></tr>';
}
if (parent.playlistinfo.type == 'stream' && parent.playlistinfo.stream) {
html += '<tr><td>'+language.gettext("info_comment")+'</td><td>'+parent.playlistinfo.stream+'</td></tr>';
}
html += '</table>';
return html;
}
function createInfoFromBeetsInfo(data) {
var html = "";
debug.log("FILE PLUGIN","Doing info from Beets server");
var file = decodeURIComponent(player.status.file);
var gibbons = [ 'year', 'genre', 'label', 'disctitle', 'encoder'];
if (!file) { return "" }
html += '<table class="motherfucker" style="width:100%">';
html += '<tr><th colspan="2">Format Information</th></tr>';
html += '<tr><td class="fil">'+language.gettext("info_file")+'</td><td>'+file;
html += ' <i>'+language.gettext("info_from_beets")+'</i>';
html = html +'</td></tr>';
html += '<tr><td class="fil">'+language.gettext("info_format")+'</td><td>'+data.format+'</td></tr>';
if (data.bitrate) html += '<tr><td class="fil">'+language.gettext("info_bitrate")+'</td><td>'+data.bitrate+'</td></tr>';
html += '<tr><td class="fil">'+language.gettext("info_samplerate")+'</td><td>'+data.samplerate+' Hz, '+data.bitdepth+' Bit, ';
if (data.channels == 1) {
html += language.gettext("info_mono");
} else if (data.channels == 2) {
html = html +language.gettext("info_stereo");
} else {
html += data.channels +' '+language.gettext("info_channels");
}
html += '</td></tr>';
$.each(gibbons, function (i,g) {
if (data[g]) html += '<tr><td class="fil">'+language.gettext("info_"+g)+'</td><td>'+data[g]+'</td></tr>';
});
if (data.composer) html += '<tr><td class="fil">'+language.gettext("info_composers")+'</td><td>'+data.composer+'</td></tr>';
if (data.comments) html += '<tr><td class="fil">'+language.gettext("info_comment")+'</td><td>'+data.comments+'</td></tr>';
html += '</table>';
return html;
}
return {
getRequirements: function(parent) {
return [];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace("FILE PLUGIN", "Creating data collection");
var self = this;
var displaying = false;
this.displayData = function() {
displaying = true;
self.doBrowserUpdate();
browser.Update(null, 'album', me, parent.nowplayingindex,
{ name: "", link: "", data: null }
);
browser.Update(null, 'artist', me, parent.nowplayingindex,
{ name: "", link: "", data: null }
);
}
this.stopDisplaying = function() {
displaying = false;
}
this.handleClick = function(source, element, event) {
if (element.hasClass("clicksetrating")) {
nowplaying.setRating(event);
} else if (element.hasClass("clickaddtocollection")) {
nowplaying.addTrackToCollection(event, parent.nowplayingindex);
} else if (element.hasClass("clickremtag")) {
nowplaying.removeTag(event, parent.nowplayingindex);
} else if (element.hasClass("clickaddtags")) {
tagAdder.show(event, parent.nowplayingindex);
}
}
this.populate = function() {
if (trackmeta.fileinfo === undefined) {
var file = parent.playlistinfo.file;
var m = file.match(/^beets:library:track(:|;)(\d+)/)
if (m && m[2] && prefs.beets_server_location != '') {
debug.trace("FILE PLUGIN","File is from beets server",m[2]);
self.updateBeetsInformation(m[2]);
} else {
setTimeout(function() {
player.controller.do_command_list([], self.updateFileInformation)
}, 1000);
}
} else {
debug.mark("FILE PLUGIN",parent.nowplayingindex,"is already populated");
}
}
this.updateFileInformation = function() {
trackmeta.fileinfo = {beets: null, player: cloneObject(player.status)};
debug.log("FILE PLUGIN","Doing update from",trackmeta);
trackmeta.lyrics = null;
player.controller.checkProgress();
self.doBrowserUpdate();
}
this.updateBeetsInformation = function(thing) {
// Get around possible same origin policy restriction by using a php script
$.getJSON('browser/backends/getBeetsInfo.php', 'uri='+thing)
.done(function(data) {
debug.trace("FILE PLUGIN",'Got info from beets server',data);
trackmeta.fileinfo = {beets: data, player: null};
if (data.lyrics) {
debug.shout("FILE PLUGIN","Got lyrics from Beets Server");
trackmeta.lyrics = data.lyrics;
} else {
trackmeta.lyrics = null;
}
self.doBrowserUpdate();
})
.fail( function() {
debug.error("FILE PLUGIN", "Error getting info from beets server");
self.updateFileInformation();
});
}
this.ratingsInfo = function() {
var html = "";
debug.shout("FILE PLUGIN","Doing the monkey spanner",trackmeta);
if (trackmeta.usermeta) {
html += '<table class="fileinfotable" style="width:100%">';
html += '<tr><th colspan="2">Collection Information</th></tr>';
if (typeof trackmeta.usermeta.Playcount != 'undefined') {
html += '<tr><td colspan="2" class="notbold">';
switch (trackmeta.usermeta.Playcount) {
case '0':
html += language.gettext('played_never');
break
case '1':
html += language.gettext('played_once');
break
case '2':
html += language.gettext('played_twice');
break
default:
html += language.gettext('played_n',[trackmeta.usermeta.Playcount]);
break
}
html += '</td></tr>';
}
if (typeof trackmeta.usermeta.Last != 'undefined' && trackmeta.usermeta.Last != 0) {
var t = parseInt(trackmeta.usermeta.Last) * 1000;
var d = new Date(t);
var s = d.toLocaleTimeString(getLocale(), { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
html += '<tr><td colspan="2" class="notbold">'+language.gettext('played_last',[s]);
html += '</td></tr>';
}
if (prefs.player_backend == 'mopidy') {
html += '<tr><td colspan="2" class="notbold">';
if (trackmeta.usermeta.isSearchResult < 2 && trackmeta.usermeta.Hidden == 0) {
html += 'This track is in the Music Collection';
} else {
html += '<span class="infoclick clickaddtocollection">This track is not in the Music Collection. Click to add it</span>';
}
html += '</td></tr>';
}
if (trackmeta.usermeta.isSearchResult < 2 && trackmeta.usermeta.Hidden == 0) {
var t = parseInt(trackmeta.usermeta.DateAdded) * 1000;
var d = new Date(t);
var s = d.toLocaleDateString(getLocale(), { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
html += '<tr><td colspan="2" class="notbold">'+language.gettext('added_on',[s]);
html += '</td></tr>';
}
html += '<tr><td>Rating:</td><td>';
html += '<i class="icon-'+trackmeta.usermeta.Rating+'-stars rating-icon-big infoclick clicksetrating"></i>';
html += '<input type="hidden" value="'+parent.nowplayingindex+'" />';
html += '</td></tr>';
html += '<tr><td style="vertical-align:top">'+language.gettext("musicbrainz_tags")+'<i class="icon-plus infoclick smallicon clickaddtags"></i></td><td>';
for(var i = 0; i < trackmeta.usermeta.Tags.length; i++) {
if (trackmeta.usermeta.Tags[i] != '') {
html += '<span class="tag">'+trackmeta.usermeta.Tags[i]+'<i class="icon-cancel-circled clickicon tagremover playlisticon"></i></span> ';
}
}
html += '</td></tr>';
}
html += '</table>';
return html;
}
this.doBrowserUpdate = function() {
if (displaying && trackmeta.fileinfo !== undefined) {
var data = '<div id="tinfobox" class="holdingcell masonified7 helpfulholder fullwidth">';
// data += '<div class="sizer"></div>';
data += '<div class="brick dingo tagholder2 tagholder">';
data += (trackmeta.fileinfo.player !== null) ? createInfoFromPlayerInfo(trackmeta.fileinfo.player, parent) : createInfoFromBeetsInfo(trackmeta.fileinfo.beets);
data += '</div>';
data += '<div class="brick dingo tagholder2 tagholder">';
data += self.ratingsInfo();
data += '</div>';
data += podComment(parent);
data += '</div>';
browser.Update(
null,
'track',
me,
parent.nowplayingindex,
{ name: trackmeta.name,
link: "",
data: data
}
);
browser.rePoint($('#tinfobox'), { itemSelector: '.brick', columnWidth: '.dingo', percentPosition: true });
}
}
}
}
}();
nowplaying.registerPlugin("file", info_file, "icon-library", "button_fileinfo");

View File

@ -0,0 +1,829 @@
var info_lastfm = function() {
var me = "lastfm";
var medebug = "LASTFM PLUGIN";
function formatLastFmError(lfmdata, type) {
if (lfmdata.errorcode() == 6) {
return '<h3 align="center">'+language.gettext('label_no'+type+'info')+'</h3>';
} else {
return '<h3 align="center">'+lfmdata.error()+'</h3>';
}
}
function sectionHeader(data) {
var html = '<div class="holdingcell">';
html += '<div class="standout stleft statsbox"><ul>';
html += '<li><b>'+language.gettext("lastfm_listeners")+'</b> '+data.listeners()+'</li>';
html += '<li><b>'+language.gettext("lastfm_plays")+'</b> '+data.playcount()+'</li>';
html += '<li><b>'+language.gettext("lastfm_yourplays")+'</b> '+data.userplaycount()+'</li>';
return html;
}
function doTags(taglist) {
debug.trace(medebug," Doing Tags");
var html = '<ul><li><b>'+language.gettext("lastfm_toptags")+'</b></li><li><table class="fullwidth">';
for(var i in taglist) {
if (taglist[i].name) {
html += '<tr><td><a href="'+taglist[i].url+'" target="_blank">'+taglist[i].name+'</a></td>';
}
}
html += '</table></li></ul>';
return html;
}
function tagsInput(type) {
var html = '<ul class="holdingcell"><li><b>'+language.gettext("lastfm_addtags")+'</b></li>';
html += '<li class="tiny">'+language.gettext("lastfm_addtagslabel")+'</li>';
html += '<li><input class="enter tiny inbrowser" type="text"></input>';
html += '<button class="infoclick clickaddtags tiny">'+language.gettext("button_add")+'</button>'+
'<i class="smallicon tright" id="tagadd'+type+'"></i></li></ul>';
return html;
}
function doUserTags(name) {
var html = '<ul><li><b>'+language.gettext("lastfm_yourtags")+'</b></li><li><table class="fullwidth" name="'+name+'tagtable">';
html += '</table></li></ul>';
return html;
}
function findTag(name, taglist) {
for(var i in taglist) {
if (name == taglist[i].name) {
debug.debug("FINDTAG", "Found tag",name);
return true;
}
}
return false;
}
function findTag2(name, table) {
var retval = false;
table.find('tr').each( function() {
var n = $(this).find('a').text();
if (n.toLowerCase() == name.toLowerCase()) {
debug.debug("FINDTAG 2",'Found Tag',name);
retval = true;
}
});
return retval;
}
function appendTag(table, name, url) {
var html = '<tr class="newtag invisible"><td><a href="'+url+'" target="_blank">'+name+'</a></td>';
html += '<td><i class="icon-cancel-circled playlisticon infoclick clickremovetag tooltip" title="'+language.gettext("lastfm_removetag")+'"></i></td>';
$('table[name="'+table+'tagtable"]').append(html);
$(".newtag").fadeIn('fast', function(){
$(this).removeClass('newtag');
});
}
function getArtistHTML(lfmdata, parent, artistmeta) {
if (lfmdata.error()) {
return formatLastFmError(lfmdata, 'artist');
}
var html = sectionHeader(lfmdata);
html += '</ul><br>';
html += doTags(lfmdata.tags());
if (lastfm.isLoggedIn()) {
html += tagsInput("artist");
html += doUserTags("artist");
}
html += '</div><div class="statsbox">';
html += '<div id="artistbio" class="minwidthed">';
html += lastfm.formatBio(lfmdata.bio(), lfmdata.url());
html += '</div></div>';
html += '</div>';
var similies = lfmdata.similar();
if (similies.length > 0 && typeof similies[0].name != 'undefined') {
html += '<div id="similarartists" class="bordered"><h3 align="center">'+language.gettext("lastfm_simar")+'</h3>';
html += '<table width="100%" cellspacing="0" cellpadding="0"><tr><td align="center"><div class="smlrtst">';
for(var i in similies) {
html += '<div class="simar">';
html += '<table><tr><td align="center">';
html += '</td></tr>';
html += '<tr><td align="center"><a href="'+similies[i].url+'" target="_blank">'+similies[i].name+'</a></td></tr>';
html += '</table>';
html += '</div>';
}
html += '</div></td></tr></table></div>';
}
return html;
}
function getAlbumHTML(lfmdata) {
if (lfmdata.error()) {
return formatLastFmError(lfmdata, 'album');
}
var html = sectionHeader(lfmdata);
html += '</ul><br>';
html += doTags(lfmdata.tags());
if (lastfm.isLoggedIn()) {
html += tagsInput("album");
html += doUserTags("album");
}
html += '</div><div class="statsbox">';
var imageurl = lfmdata.image("large");
var bigurl = lfmdata.image("mega");
if (imageurl != '') {
html += '<img class="stright standout'
if (bigurl && bigurl != imageurl) {
html += ' infoclick clickzoomimage';
}
html += ' cshrinker" src="getRemoteImage.php?url=' + imageurl + '" />';
if (bigurl && bigurl != imageurl) {
html += '<input type="hidden" value="getRemoteImage.php?url='+bigurl+'" />';
}
}
if (lfmdata.releasedate() != 'Unknown') {
html += '<p class="minwidthed">';
html += '<b>'+language.gettext("lastfm_releasedate")+' : </b>'+lfmdata.releasedate();
html += '</p>';
}
html += '<p class="minwidthed">'+lastfm.formatBio(lfmdata.bio())+'</p>';
var tracks = lfmdata.tracklisting();
debug.trace(medebug,"Track Listing",tracks);
if (tracks && tracks.length > 0) {
var dh = false;
for(var i in tracks) {
if (tracks[i].name) {
if (!dh) {
html += '<table><tr><th colspan="3">'+language.gettext("discogs_tracklisting")+'</th></tr>';
dh = true;
}
html += '<tr><td>';
if (tracks[i]['@attr']) { html += tracks[i]['@attr'].rank+':'; }
html += '</td><td>'+tracks[i].name+'</td><td>'+formatTimeString(tracks[i].duration)+'</td>';
html += '<td align="right"><a target="_blank" href="'+tracks[i].url+'"><i class="icon-lastfm-1 smallicon tooltip" title="'+language.gettext("lastfm_viewtrack")+'"></i></a></td><td align="right">';
html += '</td></tr>';
}
}
html += '</table>';
}
html += '</div>'
html += '</div>';
return html;
}
function getTrackHTML(lfmdata) {
if (lfmdata.error()) {
return formatLastFmError(lfmdata, 'track');
}
var html = sectionHeader(lfmdata);
html += '<li name="userloved">';
html = html +'</li>';
html += '</ul><br>';
html += doTags(lfmdata.tags());
if (lastfm.isLoggedIn()) {
html += tagsInput("track");
html += doUserTags("track");
}
html += '</div>';
html += '<p>'+lastfm.formatBio(lfmdata.bio())+'</p>';
html += '</div>';
return html;
}
return {
getRequirements: function(parent) {
return ['musicbrainz'];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace(medebug, "Creating data collection");
var self = this;
var displaying = false;
this.populate = function() {
$('#love').addClass('notloved').makeSpinner();
self.artist.populate();
self.album.populate();
self.track.populate();
}
this.displayData = function() {
displaying = true;
self.artist.doBrowserUpdate();
self.album.doBrowserUpdate();
self.track.doBrowserUpdate();
}
this.stopDisplaying = function() {
$('#love').stopSpinner();
displaying = false;
}
this.handleClick = function(source, element, event) {
debug.trace(medebug,parent.nowplayingindex,source,"is handling a click event");
if (element.hasClass('clickremovetag')) {
var tagname = element.parent().prev().children().text();
debug.trace(medebug,parent.nowplayingindex,source,"wants to remove tag",tagname);
self[source].removetags(tagname);
if (prefs.synctags) {
parent.setMeta('remove', 'Tags', tagname);
}
} else if (element.hasClass('clickaddtags')) {
var tagname = element.prev().val();
debug.trace(medebug,parent.nowplayingindex,source,"wants to add tags",tagname);
self[source].addtags(tagname);
if (prefs.synctags) {
parent.setMeta('set', 'Tags', tagname.split(','));
}
} else if (element.hasClass('clickzoomimage')) {
imagePopup.create(element, event, element.next().val());
} else if (element.hasClass('clickunlove')) {
self[source].unlove();
if (prefs.synclove) {
parent.setMeta('set', 'Rating', '0');
}
} else if (element.hasClass('clicklove')) {
self[source].love();
if (prefs.synclove) {
parent.setMeta('set', 'Rating', prefs.synclovevalue);
}
}
}
this.somethingfailed = function(data) {
debug.warn(medebug,"Something went wrong",data);
}
this.justaddedtags = function(type, tags) {
debug.trace(medebug,parent.nowplayingindex,"Just added or removed tags",tags,"on",type);
self[type].resetUserTags();
self[type].getUserTags();
}
this.tagAddFailed = function(type, tags) {
$("#tagadd"+type).stopSpinner();
infobar.error(language.gettext("lastfm_tagerror"));
debug.warn(medebug,"Failed to modify tags",type,tags);
}
function formatUserTagData(name, taglist, displaying) {
if (displaying) {
debug.trace("FUTD","Doing",name,"tags");
var toAdd = new Array();
var toRemove = new Array();
$('table[name="'+name+'tagtable"]').find("tr").each( function() {
if (!(findTag($(this).find('a').text(), taglist))) {
debug.trace("FUTD","Marking tag",$(this).find('a').text(),"for removal");
toRemove.push($(this));
}
});
for(var i in taglist) {
debug.trace("FUTD","Checking for addition",taglist[i].name);
if (!(findTag2(taglist[i].name, $('table[name="'+name+'tagtable"]')))) {
debug.trace("FUTD","Marking Tag",taglist[i].name,"for addition");
toAdd.push(taglist[i])
}
}
for (var i in toRemove) {
toRemove[i].fadeOut('fast', function() { $(this).remove() });
}
for (var i in toAdd) {
appendTag(name, toAdd[i].name, toAdd[i].url);
}
}
}
function doUserLoved(flag) {
debug.log("LASTFM","Doing UserLoved With Flags at",flag);
if (parent.isCurrentTrack()) {
$('#love').stopSpinner();
if (flag) {
$('#love').removeClass('notloved').attr('title', language.gettext("lastfm_unlove")).off('click').on('click', nowplaying.unlove);
} else {
$('#love').removeClass('notloved').addClass('notloved').attr('title', language.gettext("lastfm_lovethis")).off('click').on('click', nowplaying.love);
}
}
if (displaying) {
var li = $('li[name="userloved"]');
li.empty();
if (flag) {
li.append($('<b>').html(language.gettext("lastfm_loved")+': ')).append(language.gettext("label_yes")+'&nbsp;&nbsp;&nbsp;')
li.append($('<i>', {
title: language.gettext("lastfm_unlove"),
class: "icon-heart-broken smallicon infoclick clickunlove tooltip"
}));
} else {
li.append($('<b>').html(language.gettext("lastfm_loved")+': ')).append(language.gettext("label_no")+'&nbsp;&nbsp;&nbsp;')
li.append($('<i>', {
title: language.gettext("lastfm_lovethis"),
class: "icon-heart smallicon infoclick clicklove tooltip notloved"
}));
}
}
}
function getSearchArtist() {
return (albummeta.artist && albummeta.artist != "" && parent.playlistinfo.type != 'stream') ? albummeta.artist : parent.playlistinfo.trackartist;
}
function sendLastFMCorrections() {
try {
var updates = { trackartist: (parent.playlistinfo.metadata.artists.length == 1) ? self.artist.name() : parent.playlistinfo.trackartist,
album: self.album.name(),
title: self.track.name(),
image: self.album.image('mega') ? self.album.image('mega') : self.album.image('medium')
};
nowplaying.setLastFMCorrections(parent.currenttrack, updates);
} catch(err) {
debug.fail(medebug,"Not enough information to send corrections");
}
}
function sendMetadataUpdates(de) {
var lfmdata = new lfmDataExtractor(trackmeta.lastfm.track);
nowplaying.setMetadataFromLastFM(parent.nowplayingindex, {Playcount: lfmdata.userplaycount()});
}
this.artist = function() {
var retries = 10;
return {
populate: function() {
if (artistmeta.lastfm === undefined) {
debug.mark(medebug,parent.nowplayingindex,"artist is populating",artistmeta.name);
lastfm.artist.getInfo( {artist: artistmeta.name},
this.lfmResponseHandler,
this.lfmResponseHandler
);
} else {
debug.trace(medebug,parent.nowplayingindex,"artist is already populated",artistmeta.name);
}
},
lfmResponseHandler: function(data) {
debug.trace(medebug,parent.nowplayingindex,"got artist data for",artistmeta.name);
debug.trace(medebug,data);
var de = new lfmDataExtractor(data);
artistmeta.lastfm = de.getCheckedData('artist');
if (artistmeta.musicbrainz_id == "") {
var mbid = null;
try {
mbid = data.artist.mbid || null;
} catch(err) {
mbid = null;
}
debug.log(medebug,parent.nowplayingindex,"has found a musicbrainz artist ID",mbid);
artistmeta.musicbrainz_id = mbid;
}
self.artist.doBrowserUpdate();
},
tryForAllmusicImage: function() {
if (typeof artistmeta.allmusic == 'undefined' || typeof artistmeta.allmusic.artistlink === 'undefined') {
debug.shout(medebug,"Allmusic artist link not back yet");
retries--;
if (retries > 0) {
setTimeout(self.artist.tryForAllmusicImage, 2000);
} else {
debug.shout(medebug,"Artist giving up waiting for musicbrainz");
}
} else if (artistmeta.allmusic.artistlink === null) {
debug.shout(medebug,"No Allmusic artist bio link found");
} else {
debug.shout(medebug,"Getting allmusic bio from",artistmeta.allmusic.artistlink);
$.post('browser/backends/getamimage.php', {url: artistmeta.allmusic.artistlink})
.done( function(data) {
debug.log(medebug,"Got Allmusic Image", data);
if (displaying) {
var image = $('<img>', {class: "stright standout infoclick clickzoomimage cshrinker", src: "getRemoteImage.php?url="+data}).insertBefore('#artistbio');
var input = $('<input>', {type: "hidden", value: "getRemoteImage.php?url="+data});
};
})
.fail( function() {
debug.log(medebug,"Didn't Get Allmusic Image");
});
}
},
doBrowserUpdate: function() {
if (displaying && artistmeta.lastfm !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"artist was asked to display");
var lfmdata = new lfmDataExtractor(artistmeta.lastfm.artist);
var accepted = browser.Update(
null,
'artist',
me,
parent.nowplayingindex,
{ name: self.artist.name(),
link: lfmdata.url(),
data: getArtistHTML(lfmdata, parent, artistmeta)
}
);
if (accepted && lastfm.isLoggedIn() && !lfmdata.error()) {
self.artist.getUserTags();
self.artist.tryForAllmusicImage();
}
}
},
name: function() {
try {
return artistmeta.lastfm.artist.name || artistmeta.name;
} catch(err) {
return artistmeta.name;
}
},
getFullBio: function(callback, failcallback) {
debug.shout(medebug,parent.nowplayingindex,"Not Getting Bio URL:", artistmeta.lastfm.artist.url);
},
updateBio: function(data) {
if (displaying) {
$("#artistbio").html(lastfm.formatBio(data, null));
}
},
resetUserTags: function() {
artistmeta.lastfm.usertags = null;
},
getUserTags: function() {
debug.debug(medebug,parent.nowplayingindex,"Getting Artist User Tags");
if (artistmeta.lastfm.usertags) {
formatUserTagData('artist', artistmeta.lastfm.usertags, displaying);
} else {
var options = { artist: self.artist.name() };
if (artistmeta.musicbrainz_id != "") {
options.mbid = artistmeta.musicbrainz_id;
}
lastfm.artist.getTags(
options,
self.artist.gotUserTags,
self.artist.somethingfailed
);
}
},
somethingfailed: function(data) {
$("#tagaddartist").stopSpinner();
debug.warn(medebug,"Something went wrong getting artist user tags",data);
},
gotUserTags: function(data) {
$("#tagaddartist").stopSpinner();
var de = new lfmDataExtractor(data);
artistmeta.lastfm.usertags = de.tags();
formatUserTagData('artist', artistmeta.lastfm.usertags, displaying);
},
addtags: function(tags) {
$("#tagaddartist").makeSpinner();
lastfm.artist.addTags({ artist: self.artist.name(),
tags: tags},
self.justaddedtags,
self.tagAddFailed
);
},
removetags: function(tags) {
$("#tagaddartist").makeSpinner();
lastfm.artist.removeTag({artist: self.artist.name(),
tag: tags},
self.justaddedtags,
self.tagAddFailed
);
}
}
}();
this.album = function() {
return {
populate: function() {
if (albummeta.lastfm === undefined) {
debug.mark(medebug,"Getting last.fm data for album",albummeta.name);
if (parent.playlistinfo.type == 'stream') {
lastfm.artist.getInfo({ artist: albummeta.name },
this.lfmArtistResponseHandler,
this.lfmArtistResponseHandler );
} else {
lastfm.album.getInfo({ artist: getSearchArtist(),
album: albummeta.name},
this.lfmResponseHandler,
this.lfmResponseHandler );
}
} else {
debug.trace(medebug,"Album is already populated",albummeta.name);
}
},
lfmResponseHandler: function(data) {
debug.trace(medebug,"Got Album Info for",albummeta.name);
debug.debug(medebug, data);
var de = new lfmDataExtractor(data);
albummeta.lastfm = de.getCheckedData('album');
if (albummeta.musicbrainz_id == "") {
var mbid = null;
try {
mbid = data.album.mbid || null;
} catch(err) {
mbid = null;
}
if (mbid !== null) {
debug.log(medebug,parent.nowplayingindex,"has found a musicbrainz album ID",mbid);
nowplaying.updateAlbumMBID(parent.nowplayingindex,mbid);
}
albummeta.musicbrainz_id = mbid;
}
self.album.doBrowserUpdate();
},
lfmArtistResponseHandler: function(data) {
debug.trace(medebug,"Got Album/Artist Info for",albummeta.name);
debug.debug(medebug, data);
var de = new lfmDataExtractor(data);
albummeta.lastfm = de.getCheckedData('artist');
albummeta.musicbrainz_id = null;
self.album.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && albummeta.lastfm !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"album was asked to display");
var lfmdata = (parent.playlistinfo.type == 'stream') ? new lfmDataExtractor(albummeta.lastfm.artist) : new lfmDataExtractor(albummeta.lastfm.album);
var accepted = browser.Update(
null,
'album',
me,
parent.nowplayingindex,
{ name: lfmdata.name() || albummeta.name,
link: lfmdata.url(),
data: (parent.playlistinfo.type == 'stream') ? getArtistHTML(lfmdata) : getAlbumHTML(lfmdata)
}
);
if (accepted && lastfm.isLoggedIn() && !lfmdata.error()) {
self.album.getUserTags();
}
}
},
name: function() {
try {
return albummeta.lastfm.album.name || albummeta.name;
} catch(err) {
return albummeta.name;
}
},
image: function(size) {
if (albummeta.lastfm.album) {
var lfmdata = new lfmDataExtractor(albummeta.lastfm.album);
return lfmdata.image(size);
}
return "";
},
resetUserTags: function() {
albummeta.lastfm.usertags = null;
},
getUserTags: function() {
debug.debug(medebug,parent.nowplayingindex,"Getting Album User Tags");
if (albummeta.lastfm.usertags) {
formatUserTagData('album', albummeta.lastfm.usertags, displaying);
} else {
var options = { artist: getSearchArtist(), album: self.album.name() };
if (albummeta.musicbrainz_id != "" && albummeta.musicbrainz_id != null) {
options.mbid = albummeta.musicbrainz_id;
}
lastfm.album.getTags(
options,
self.album.gotUserTags,
self.album.somethingfailed
);
}
},
somethingfailed: function(data) {
$("#tagaddalbum").stopSpinner();
debug.warn(medebug,"Something went wrong getting album user tags",data);
},
gotUserTags: function(data) {
$("#tagaddalbum").stopSpinner();
var de = new lfmDataExtractor(data);
albummeta.lastfm.usertags = de.tags();
formatUserTagData('album', albummeta.lastfm.usertags, displaying);
},
addtags: function(tags) {
$("#tagaddalbum").makeSpinner();
lastfm.album.addTags({ artist: getSearchArtist(),
album: self.album.name(),
tags: tags},
self.justaddedtags,
self.tagAddFailed
);
},
removetags: function(tags) {
$("#tagaddalbum").makeSpinner();
lastfm.album.removeTag({ artist: getSearchArtist(),
album: self.album.name(),
tag: tags},
self.justaddedtags,
self.tagAddFailed
);
}
}
}();
this.track = function() {
return {
populate: function() {
if (trackmeta.lastfm === undefined) {
debug.mark(medebug,parent.nowplayingindex,"Getting last.fm data for track",trackmeta.name);
lastfm.track.getInfo( { artist: getSearchArtist(), track: trackmeta.name },
this.lfmResponseHandler,
this.lfmResponseHandler );
} else {
debug.trace(medebug,parent.nowplayingindex,"Track is already populated",trackmeta.name);
sendLastFMCorrections();
}
},
lfmResponseHandler: function(data) {
debug.trace(medebug,parent.nowplayingindex,"Got Track Info for",trackmeta.name);
debug.debug(medebug, data);
var de = new lfmDataExtractor(data);
trackmeta.lastfm = de.getCheckedData('track');
if (trackmeta.musicbrainz_id == "") {
var mbid = null;
try {
mbid = data.track.mbid || null;
} catch(err) {
mbid = null;
}
debug.trace(medebug,parent.nowplayingindex,"has found a musicbrainz track ID",mbid);
trackmeta.musicbrainz_id = mbid;
}
sendLastFMCorrections();
sendMetadataUpdates();
self.track.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && trackmeta.lastfm !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"track was asked to display");
var lfmdata = new lfmDataExtractor(trackmeta.lastfm.track);
var accepted = browser.Update(
null,
'track',
me,
parent.nowplayingindex,
{ name: self.track.name(),
link: lfmdata.url(),
data: getTrackHTML(lfmdata)
}
);
if (accepted && lastfm.isLoggedIn() && !lfmdata.error()) {
self.track.getUserTags();
}
}
if (trackmeta.lastfm !== undefined) {
var lfmdata = new lfmDataExtractor(trackmeta.lastfm.track);
doUserLoved(lfmdata.userloved());
}
},
name: function() {
try {
return trackmeta.lastfm.track.name || trackmeta.name;
} catch(err) {
return trackmeta.name;
}
},
resetUserTags: function() {
trackmeta.lastfm.usertags = null;
},
getUserTags: function() {
debug.debug(medebug,parent.nowplayingindex,"Getting Track User Tags");
if (trackmeta.lastfm.usertags) {
formatUserTagData('track', trackmeta.lastfm.usertags, displaying);
} else {
var options = { artist: self.artist.name(), track: self.track.name() };
if (trackmeta.musicbrainz_id != "" && trackmeta.musicbrainz_id != null) {
options.mbid = trackmeta.musicbrainz_id;
}
lastfm.track.getTags(
options,
self.track.gotUserTags,
self.track.somethingfailed,
0
);
}
},
somethingfailed: function(data) {
$("#tagaddtrack").stopSpinner();
debug.warn(medebug,"Something went wrong getting track user tags",data);
},
gotUserTags: function(data) {
$("#tagaddtrack").stopSpinner();
var de = new lfmDataExtractor(data);
trackmeta.lastfm.usertags = de.tags();
formatUserTagData('track', trackmeta.lastfm.usertags, displaying);
},
addtags: function(tags) {
$("#tagaddtrack").makeSpinner();
lastfm.track.addTags({ artist: self.artist.name(),
track: self.track.name(),
tags: tags},
self.justaddedtags,
self.tagAddFailed
);
},
removetags: function(tags) {
if (findTag2(tags, $('table[name="tracktagtable"]'))) {
$("#tagaddtrack").makeSpinner();
lastfm.track.removeTag({ artist: self.artist.name(),
track: self.track.name(),
tag: tags},
self.justaddedtags,
self.tagAddFailed
);
} else {
debug.warn(medebug, "Tag",tags,"not found on track");
}
},
love: function() {
lastfm.track.love({ track: self.track.name(), artist: self.artist.name() }, self.track.donelove);
},
unlove: function(callback) {
lastfm.track.unlove({ track: self.track.name(), artist: self.artist.name() }, self.track.donelove);
},
unloveifloved: function() {
if (trackmeta.lastfm.track.userloved == 1) {
self.track.unlove();
}
},
donelove: function(loved) {
if (loved) {
// Rather than re-get all the details, we can just edit the track data directly.
trackmeta.lastfm.track.userloved = 1;
if (prefs.autotagname != '') {
self.track.addtags(prefs.autotagname);
if (prefs.synctags && prefs.synclove) {
parent.setMeta('set', 'Tags', [prefs.autotagname]);
}
}
doUserLoved(true)
} else {
trackmeta.lastfm.track.userloved = 0;
if (prefs.autotagname != '') {
self.track.removetags(prefs.autotagname);
if (prefs.synctags && prefs.synclove) {
parent.setMeta('remove', 'Tags', prefs.autotagname);
}
}
doUserLoved(false)
}
}
}
}();
}
}
}();
nowplaying.registerPlugin("lastfm", info_lastfm, "icon-lastfm-1", "button_infolastfm");

View File

@ -0,0 +1,420 @@
var info_wikipedia = function() {
var me = "wikipedia";
function formatWiki(xml) {
var xml_node = $(xml);
var html = xml_node.find('parse > text').text();
var domain = xml_node.find('rompr > domain').text();
var jq = $('<div>'+html+'</div>');
// Remove unwanted edit links
jq.find("span.editsection").remove();
jq.find("a.edit-page").remove();
// Make external links open in a new tab
jq.find("a[href^='http:']").attr("target", "_blank");
jq.find("a[href^='//']").attr("target", "_blank");
jq.find("a[href^='/w/']").each( function() {
var ref = $(this).attr('href');
$(this).attr('href', 'http://'+domain+'.wikipedia.org'+ref);
$(this).attr("target", "_blank");
});
// Make the contents table links work
jq.find("a[href^='#']").each( function() {
if (!$(this).hasClass('infoclick')) {
var ref = $(this).attr('href');
$(this).attr('name', ref);
$(this).attr("href", "#");
$(this).addClass("infoclick clickwikicontents");
}
});
// Redirect wiki image links so they go to our function to be displayed
jq.find("a.image[href^='/wiki/']").each( function() {
var ref = $(this).attr('href');
$(this).attr('href', '#');
$(this).attr('name', domain+'.wikipedia.org/'+ref.replace(/\/wiki\//,''));
$(this).addClass('infoclick clickwikimedia');
});
jq.find("a.image[href^='//commons.wikimedia.org/']").each( function() {
var ref = $(this).attr('href');
$(this).attr('href', '#');
$(this).attr('name', 'commons.wikimedia.org/'+ref.replace(/\/\/commons\.wikimedia\.org\/wiki\//,''));
$(this).addClass('infoclick clickwikimedia');
});
// Redirect intra-wikipedia links so they go to our function
jq.find("a[href^='/wiki/']").each( function() {
var ref = $(this).attr('href');
$(this).attr('href', '#');
$(this).attr('name', domain+'/'+ref.replace(/\/wiki\//,''));
$(this).addClass('infoclick clickwikilink');
});
// Remove inline colour styles on elements.
// We do background color twice because some elements have been found
// to have 2 background color styles applied.
// if (prefs.theme == "Darkness.css" || prefs.theme == "TheBlues.css" || prefs.theme == "DarknessHiDPI.css" ) {
jq.find('[style*=background-color]').removeInlineCss('background-color');
jq.find('[style*=background-color]').removeInlineCss('background-color');
jq.find('[style*=background]').removeInlineCss('background');
jq.find('[style*=color]').removeInlineCss('color');
// }
// Remove these bits because they're a pain in the arse
jq.find("li[class|='nv']").remove();
return jq.html();
}
function formatLink(xml) {
var xml_node = $('api',xml);
return 'http://'+xml_node.find('rompr > domain').text()+'.wikipedia.org/wiki/'+xml_node.find('rompr > page').text();
}
function formatPage(xml) {
var xml_node = $('api',xml);
var page = xml_node.find('rompr > page').text();
return page.replace(/_/g, ' ');
}
return {
getRequirements: function(parent) {
return ["musicbrainz"];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace("WIKI PLUGIN", "Creating data collection");
var self = this;
var displaying = false;
this.populate = function() {
self.artist.populate();
self.album.populate();
self.track.populate();
}
this.displayData = function() {
displaying = true;
self.artist.doBrowserUpdate();
self.album.doBrowserUpdate();
self.track.doBrowserUpdate();
}
this.stopDisplaying = function(waitingon) {
displaying = false;
}
this.handleClick = function(source, element, event) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,source,"is handling a click event");
if (element.hasClass('clickwikimedia')) {
wikipedia.wikiMediaPopup(element, event);
} else if (element.hasClass('clickwikilink')) {
var link = decodeURIComponent(element.attr('name'));
debug.trace("WIKI PLUGIN",parent.nowplayingindex,source,"clicked a wiki link",link);
self[source].followLink(link);
} else if (element.hasClass('clickwikicontents')) {
var section = element.attr('name');
debug.trace("WIKI PLUGIN",parent.nowplayingindex,source,"clicked a contents link",section);
layoutProcessor.goToBrowserSection(section);
}
}
this.wikiGotFailed = function(data) {
debug.warn("WIKI PLUGIN", "Failed to get Wiki Link",data);
}
function getSearchArtist() {
return (albummeta.artist && albummeta.artist != "") ? albummeta.artist : parent.playlistinfo.trackartist;
}
this.artist = function() {
var retries = 10;
return {
populate: function() {
if (artistmeta.wikipedia === undefined) {
artistmeta.wikipedia = {};
}
if (artistmeta.wikipedia.artistinfo === undefined) {
if (artistmeta.wikipedia.artistlink === undefined) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Artist asked to populate but no link yet");
retries--;
if (retries == 0) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Artist giving up waiting for poxy musicbrainz");
artistmeta.wikipedia.artistlink = null;
setTimeout(self.artist.populate, 200);
} else {
setTimeout(self.artist.populate, 2000);
}
return;
}
if (artistmeta.wikipedia.artistlink === null) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Artist asked to populate but no link could be found. Trying a search");
wikipedia.search({ artist: artistmeta.name,
disambiguation: artistmeta.disambiguation || ""
},
self.artist.wikiResponseHandler,
self.artist.wikiResponseHandler);
return;
}
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"artist is populating",artistmeta.wikipedia.artistlink);
wikipedia.getFullUri({ uri: artistmeta.wikipedia.artistlink,
term: artistmeta.name
},
self.artist.wikiResponseHandler,
self.artist.wikiResponseHandler);
} else {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"artist is already populated",artistmeta.wikipedia.artistlink);
}
},
wikiResponseHandler: function(data) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"got artist data for",artistmeta.name,data);
if (data) {
artistmeta.wikipedia.artistinfo = formatWiki(data);
artistmeta.wikipedia.artistlink = formatLink(data);
} else {
artistmeta.wikipedia.artistinfo = '<h3 align="center">'+language.gettext("wiki_nothing")+'</h3>';
artistmeta.wikipedia.artistlink = null;
}
self.artist.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && artistmeta.wikipedia.artistinfo !== undefined) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"artist was asked to display");
browser.Update(
null,
'artist',
me,
parent.nowplayingindex,
{ name: artistmeta.name,
link: artistmeta.wikipedia.artistlink,
data: artistmeta.wikipedia.artistinfo
}
);
}
},
followLink: function(link) {
wikipedia.getWiki(link, self.artist.gotWikiLink, self.wikiGotFailed);
},
gotWikiLink: function(data) {
browser.speciaUpdate(
me,
'artist',
{ name: formatPage(data),
link: formatLink(data),
data: formatWiki(data)
}
);
}
}
}();
this.album = function() {
var retries = 12;
return {
populate: function() {
if (albummeta.wikipedia === undefined) {
albummeta.wikipedia = {};
}
if (albummeta.wikipedia.albumdata === undefined) {
if (albummeta.wikipedia.albumlink === undefined) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Album asked to populate but no link yet");
retries--;
if (retries == 0) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Album giving up waiting for poxy musicbrainz");
albummeta.wikipedia.albumlink = null;
setTimeout(self.album.populate, 200);
} else {
setTimeout(self.album.populate, 2000);
}
return;
}
if (albummeta.wikipedia.albumlink === null) {
if (albummeta.musicbrainz.album_releasegroupid !== null) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"No album link found ... trying the album release group");
musicbrainz.releasegroup.getInfo(albummeta.musicbrainz.album_releasegroupid, '', self.album.mbRgHandler, self.album.mbRgHandler);
} else {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"No album link or release group link ... trying a search");
wikipedia.search({album: albummeta.name, albumartist: getSearchArtist()}, self.album.wikiResponseHandler, self.album.wikiResponseHandler);
}
return;
}
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"album is populating",albummeta.wikipedia.albumlink);
wikipedia.getFullUri({ uri: albummeta.wikipedia.albumlink,
term: albummeta.name
},
self.album.wikiResponseHandler,
self.album.wikiResponseHandler
);
} else {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"album is already populated",albummeta.wikipedia.albumlink);
}
},
wikiResponseHandler: function(data) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"got album data for",albummeta.name);
if (data) {
albummeta.wikipedia.albumdata = formatWiki(data);
albummeta.wikipedia.albumlink = formatLink(data);
} else {
albummeta.wikipedia.albumdata = '<h3 align="center">'+language.gettext("wiki_nothing")+'</h3>';
albummeta.wikipedia.albumlink = null;
}
self.album.doBrowserUpdate();
},
mbRgHandler: function(data) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"got musicbrainz release group data for",albummeta.name, data);
if (data.error) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex," ... MB error",data);
} else {
for (var i in data.relations) {
if (data.relations[i].type == "wikipedia") {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"has found a Wikipedia album link",data.relations[i].url.resource);
albummeta.wikipedia.albumlink = data.relations[i].url.resource;
self.album.populate();
return;
}
}
}
albummeta.wikipedia.albumlink = null;
albummeta.musicbrainz.album_releasegroupid = null;
self.album.populate();
},
doBrowserUpdate: function() {
if (displaying && albummeta.wikipedia.albumdata !== undefined) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"album was asked to display");
browser.Update(
null,
'album',
me,
parent.nowplayingindex,
{ name: albummeta.name,
link: albummeta.wikipedia.albumlink,
data: albummeta.wikipedia.albumdata
}
);
}
},
followLink: function(link) {
wikipedia.getWiki(link, self.album.gotWikiLink, self.wikiGotFailed);
},
gotWikiLink: function(data) {
browser.speciaUpdate(me, 'album', { name: formatPage(data),
link: formatLink(data),
data: formatWiki(data)});
}
}
}();
this.track = function() {
var retries = 15;
return {
populate: function() {
if (trackmeta.wikipedia === undefined) {
trackmeta.wikipedia = {};
}
if (trackmeta.wikipedia.trackdata === undefined) {
if (trackmeta.wikipedia.tracklink === undefined) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"track asked to populate but no link yet");
retries--;
if (retries == 0) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"Track giving up waiting for poxy musicbrainz");
trackmeta.wikipedia.tracklink = null;
setTimeout(self.track.populate, 200);
} else {
setTimeout(self.track.populate, 2000);
}
return;
}
if (trackmeta.wikipedia.tracklink === null) {
debug.shout("WIKI PLUGIN",parent.nowplayingindex,"track asked to populate but no link could be found. Trying a search");
wikipedia.search({track: trackmeta.name, trackartist: parent.playlistinfo.trackartist}, self.track.wikiResponseHandler, self.track.wikiResponseHandler);
return;
}
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"track is populating",trackmeta.wikipedia.tracklink);
wikipedia.getFullUri({ uri: trackmeta.wikipedia.tracklink,
term: trackmeta.name
},
self.track.wikiResponseHandler,
self.track.wikiResponseHandler
);
} else {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"track is already populated",trackmeta.wikipedia.tracklink);
}
},
wikiResponseHandler: function(data) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"got track data for",trackmeta.name);
if (data) {
trackmeta.wikipedia.trackdata = formatWiki(data);
trackmeta.wikipedia.tracklink = formatLink(data);
} else {
trackmeta.wikipedia.trackdata = '<h3 align="center">'+language.gettext("wiki_nothing")+'</h3>';
trackmeta.wikipedia.tracklink = null;
}
self.track.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && trackmeta.wikipedia.trackdata !== undefined) {
debug.trace("WIKI PLUGIN",parent.nowplayingindex,"track was asked to display");
browser.Update(
null,
'track',
me,
parent.nowplayingindex,
{ name: trackmeta.name,
link: trackmeta.wikipedia.tracklink,
data: trackmeta.wikipedia.trackdata
}
);
}
},
followLink: function(link) {
wikipedia.getWiki(link, self.track.gotWikiLink, self.wikiGotFailed);
},
gotWikiLink: function(data) {
browser.speciaUpdate( me, 'track',
{ name: formatPage(data),
link: formatLink(data),
data: formatWiki(data)
}
);
}
}
}();
}
}
}();
nowplaying.registerPlugin("wikipedia", info_wikipedia, "icon-wikipedia", "button_wikipedia");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
var info_lyrics = function() {
var me = "lyrics";
return {
getRequirements: function(parent) {
return ['file'];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace("LYRICS PLUGIN", "Creating data collection");
var self = this;
var displaying = false;
function formatLyrics(data) {
debug.trace("LYRICS PLUGIN","Formatting Lyrics");
if (data) {
data = data.replace(/^(\w)/, '<font size="120%">$1</font>')
data = data.replace(/\n/g, '<br>');
}
return '<div class="lyrics"><h2 align="center">'+language.gettext("lyrics_lyrics")+'</h2><p>'+data+'</p></div>';
}
function getSearchArtist() {
return (albummeta.artist && albummeta.artist != "") ? albummeta.artist : parent.playlistinfo.trackartist;
}
this.displayData = function() {
displaying = true;
browser.Update(null, 'album', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
browser.Update(null, 'artist', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
self.doBrowserUpdate();
}
this.stopDisplaying = function() {
displaying = false;
}
this.startAfterSpecial = function() {
}
this.tryReadingTags = function() {
if (prefs.music_directory_albumart == "") {
trackmeta.lyrics = '<h3 align=center>'+language.gettext("lyrics_nonefound")+'</h3><p>'+language.gettext("lyrics_nopath")+'</p>';
self.doBrowserUpdate();
} else {
$.post("browser/backends/getLyrics.php", {file: player.status.file, artist: getSearchArtist(), song: trackmeta.name})
.done(function(data) {
debug.trace("LYRICS",data);
trackmeta.lyrics = data;
self.doBrowserUpdate();
});
}
}
this.populate = function() {
if (trackmeta.lyrics === undefined) {
debug.trace("LYRICS PLUGIN",parent.nowplayingindex,"No lyrics yet, trying again in 1 second");
setTimeout(self.populate, 1000);
return;
}
if (trackmeta.lyrics === null) {
self.tryReadingTags();
} else {
self.doBrowserUpdate();
}
}
this.doBrowserUpdate = function() {
if (displaying && trackmeta.lyrics !== undefined && trackmeta.lyrics !== null) {
browser.Update(null, 'track', me, parent.nowplayingindex, { name: trackmeta.name,
link: "",
data: formatLyrics(trackmeta.lyrics)
}
);
}
}
}
}
}();
nowplaying.registerPlugin("lyrics", info_lyrics, "icon-doc-text-1", "button_lyrics");

View File

@ -0,0 +1,297 @@
var info_soundcloud = function() {
var me = "soundcloud";
var tempcanvas = document.createElement('canvas');
var scImg = new Image();
function getTrackHTML(data) {
debug.trace("SOUNDCLOUD PLUGIN","Creating track HTML from",data);
var html = '<div class="containerbox info-detail-layout">';
html += '<div class="info-box-fixed info-border-right info-box-list">';
if (data.artwork_url) {
html += '<img src="' + data.artwork_url + '" class="clrboth" style="margin:8px" />';
}
html += '<ul><li><h3>'+language.gettext("soundcloud_trackinfo")+':</h3></li>';
html += '<li><b>'+language.gettext("soundcloud_plays")+':</b> '+formatSCMessyBits(data.playback_count)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_downloads")+':</b> '+formatSCMessyBits(data.download_count)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_faves")+':</b> '+formatSCMessyBits(data.favoritings_count)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_state")+'</b> '+formatSCMessyBits(data.state)+'</li>';
html += '<li><b>'+language.gettext("info_genre")+'</b> '+formatSCMessyBits(data.genre)+'</li>';
html += '<li><b>'+language.gettext("info_label")+'</b> '+formatSCMessyBits(data.label_name)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_license")+':</b> '+formatSCMessyBits(data.license)+'</li>';
if (data.purchase_url) {
html += '<li><b><a href="' + data.purchase_url + '" target="_blank">'+language.gettext("soundcloud_buy")+'</a></b></li>';
}
html += '<li><a href="' + data.permalink_url + '" title="View In New Tab" target="_blank"><b>'+language.gettext("soundcloud_view")+'</b></a></li>';
html += '</ul>';
html += '</div>';
html += '<div class="info-box-expand stumpy">';
html += '<div id="similarartists" class="bordered" style="position:relative">'+
'<div id="scprog"></div>'+
'<img id="gosblin" />'+
'</div>';
var d = formatSCMessyBits(data.description);
d = d.replace(/\n/g, "</p><p>");
html += '<p>'+d+'</p>';
html += '</div>';
html += '</div>';
return html;
}
function getArtistHTML(data) {
debug.trace("SOUNDCLOUD PLUGIN","Creating artist HTML from",data);
var html = '<div class="containerbox info-detail-layout">';
html += '<div class="info-box-fixed info-border-right info-box-list">';
if (data.avatar_url) {
html += '<img src="' + data.avatar_url + '" class="clrboth" style="margin:8px" />';
}
html += '<ul><li><h3>'+language.gettext("soundcloud_user")+':</h3></li>';
html += '<li><b>'+language.gettext("soundcloud_fullname")+':</b> '+formatSCMessyBits(data.full_name)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_Country")+':</b> '+formatSCMessyBits(data.country)+'</li>';
html += '<li><b>'+language.gettext("soundcloud_city")+':</b> '+formatSCMessyBits(data.city)+'</li>';
if (data.website) {
html += '<li><b><a href="' + data.website + '" target="_blank">'+language.gettext("soundcloud_website")+'</a></b></li>';
}
html += '</ul>';
html += '</div>';
html += '<div class="info-box-expand stumpy">';
var f = formatSCMessyBits(data.description)
f = f.replace(/\n/g, "</p><p>");
html += '<p>'+ f +'</p>';
html += '</div>';
html += '</div>';
return html;
}
function formatSCMessyBits(bits) {
try {
if (bits) {
return bits.fixDodgyLinks();
} else {
return "";
}
} catch(err) {
return "";
}
}
return {
getRequirements: function(parent) {
return [];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.log("SOUNDCLOUD PLUGIN", "Creating data collection");
var self = this;
var wi = 0;
var displaying = false;
this.populate = function() {
self.track.populate();
}
this.displayData = function(waitingon) {
displaying = true;
self.artist.doBrowserUpdate();
self.album.doBrowserUpdate();
self.track.doBrowserUpdate();
}
this.stopDisplaying = function(waitingon) {
displaying = false;
}
this.progressUpdate = function(percent) {
self.track.updateProgress(percent);
}
this.artist = function() {
return {
populate: function() {
if (trackmeta.soundcloud.track.error) {
browser.Update(null, 'artist', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
} else {
if (artistmeta.soundcloud.artist === undefined) {
debug.log("SOUNDCLOUD PLUGIN","Artist is populating");
soundcloud.getUserInfo(artistmeta.soundcloud.id, self.artist.scResponseHandler);
}
}
},
scResponseHandler: function(data) {
artistmeta.soundcloud.artist = data;
self.artist.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && trackmeta.soundcloud.track !== undefined) {
if (trackmeta.soundcloud.track.error) {
browser.Update(null, 'artist', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
} else if (artistmeta.soundcloud !== undefined &&
artistmeta.soundcloud.artist !== undefined) {
if (artistmeta.soundcloud.artist.error) {
browser.Update(null, 'artist', me, parent.nowplayingindex, {name: artistmeta.name,
link: "",
data: '<h3 align="center">'+artistmeta.soundcloud.artist.error+'</h3>'
}
);
} else {
var accepted = browser.Update(null, 'artist', me, parent.nowplayingindex, { name: artistmeta.soundcloud.artist.username,
link: artistmeta.soundcloud.artist.permalink_url,
data: getArtistHTML(artistmeta.soundcloud.artist)
}
);
}
}
}
}
}
}();
this.album = function() {
return {
doBrowserUpdate: function() {
if (displaying) {
browser.Update(null, 'album', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
}
}
}
}();
// I do not have a pet alligator
this.track = function() {
return {
populate: function() {
if (artistmeta.soundcloud === undefined) {
artistmeta.soundcloud = {};
}
if (trackmeta.soundcloud === undefined) {
trackmeta.soundcloud = {};
var t = parent.playlistinfo.file;
if (t.substring(0,11) == 'soundcloud:') {
soundcloud.getTrackInfo(parent.playlistinfo.file, self.track.scResponseHandler);
} else if (t.match(/api\.soundcloud\.com\/tracks\/(\d+)\//)) {
var sc = t.match(/api\.soundcloud\.com\/tracks\/(\d+)\//);
soundcloud.getTrackInfo(sc[1], self.track.scResponseHandler);
} else if (t.match(/feeds\.soundcloud\.com\/stream\/(\d+)/)) {
var sc = t.match(/feeds\.soundcloud\.com\/stream\/(\d+)/);
soundcloud.getTrackInfo(sc[1], self.track.scResponseHandler);
} else {
trackmeta.soundcloud.track = {error: language.gettext("soundcloud_not")};
self.artist.populate();
self.track.doBrowserUpdate();
}
} else {
self.artist.populate();
}
},
scResponseHandler: function(data) {
debug.log("SOUNDCLOUD PLUGIN","Got SoundCloud Track Data:",data);
trackmeta.soundcloud.track = data;
artistmeta.soundcloud.id = data.user_id;
self.artist.populate();
self.track.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && trackmeta.soundcloud.track !== undefined) {
debug.log("SOUNDCLOUD PLUGIN","Track was asked to display");
if (trackmeta.soundcloud.track.error) {
browser.Update(null, 'track', me, parent.nowplayingindex, { name: trackmeta.name,
link: "",
data: '<h3 align="center">'+trackmeta.soundcloud.track.error+'</h3>'
}
);
} else {
var accepted = browser.Update(null, 'track', me, parent.nowplayingindex, { name: trackmeta.name,
link: trackmeta.soundcloud.track.permalink_url,
data: getTrackHTML(trackmeta.soundcloud.track)
}
);
if (accepted) {
debug.log("SOUNDCLOUD PLUGIN","Getting Track Waveform",formatSCMessyBits(trackmeta.soundcloud.track.waveform_url));
scImg.onload = self.track.doSCImageStuff;
scImg.src = "getRemoteImage.php?url="+formatSCMessyBits(trackmeta.soundcloud.track.waveform_url);
}
}
}
},
doSCImageStuff: function() {
// The soundcloud waveform is a png where the waveform itself is transparent
// and has a grey-ish border. We want an image with a gradient for the waveform
// and a transparent border.
tempcanvas.width = scImg.width;
tempcanvas.height = scImg.height;
var ctx = tempcanvas.getContext("2d");
ctx.clearRect(0,0,tempcanvas.width,tempcanvas.height);
// Fill tempcanvas with a linear gradient
var gradient = ctx.createLinearGradient(0,0,0,tempcanvas.height);
gradient.addColorStop(0,'rgba(255,82,0,1)');
gradient.addColorStop(0.6,'rgba(150, 48, 0, 1)');
gradient.addColorStop(1,'rgba(100, 25, 0, 0.1)');
ctx.fillStyle = gradient;
ctx.fillRect(0,0,tempcanvas.width,tempcanvas.height);
// Plop the image over the top.
ctx.drawImage(scImg,0,0,tempcanvas.width,tempcanvas.height);
// Now translate all the grey pixels into transparent ones
var pixels = ctx.getImageData(0,0,tempcanvas.width,tempcanvas.height);
var data = pixels.data;
for (var i = 0; i<data.length; i += 4) {
if (data[i] == data[i+1] && data[i+1] == data[i+2]) {
data[i+3] = 0;
}
}
ctx.clearRect(0,0,tempcanvas.width,tempcanvas.height);
ctx.putImageData(pixels,0,0);
$("#gosblin").attr("src", tempcanvas.toDataURL());
},
updateProgress: function(percent) {
if (displaying) {
var w = Math.round($("#similarartists").width()*percent/100);
if (percent == 0) {
var h = 0;
} else {
var h = $("#similarartists").height() - 8;
}
$("#scprog").css({left: w+"px", height: h+"px"});
}
}
}
}();
}
}
}();
nowplaying.registerPlugin("soundcloud", info_soundcloud, "icon-soundcloud-circled", "button_soundcloud");

View File

@ -0,0 +1,703 @@
var info_spotify = function() {
var me = "spotify";
var medebug = "SPOTIFY PLUGIN";
var maxwidth = 300;
function getTrackHTML(data) {
debug.trace(medebug,"Making Track Info From",data);
if (data.error) {
return '<h3 align="center">'+data.error+'</h3>';
}
var h = '<div class="holdingcell">';
h += '<div class="standout stleft statsbox">';
h += '<ul>';
h += '<li><b>'+language.gettext("label_pop")+': </b>'+data.popularity+'</li>';
if (player.canPlay('spotify')) {
h += '<li>'+
'<div class="containerbox menuitem infoclick clickstarttrackradio" style="padding-left:0px">'+
'<div class="fixed alignmid"><i class="icon-wifi smallicon"></i></div>'+
'<div class="expand">'+language.gettext('label_radio_recommend',['Track'])+'</div>'+
'</div></li>';
}
h += '</ul>';
h += '</div>';
if (data.explicit) {
h += '<i class="icon-explicit stright standout"></i>';
}
h += '</div>';
h += '<div id="helpful_title"></div>';
h += '<div id="helpful_tracks" class="holdingcell selecotron masonified4"></div>';
return h;
}
function getAlbumHTML(data) {
debug.trace(medebug,"Making Album Info From",data);
if (data.error) {
return '<h3 align="center">'+data.error+'</h3>';
}
var html = '<div class="containerbox standout info-detail-layout">';
html += '<div class="info-box-expand info-box-list info-border-right">';
html += '<ul>';
html += '<li>'+language.gettext("label_pop")+': '+data.popularity+'</li>';
html += '<li>'+language.gettext("lastfm_releasedate")+': '+data.release_date+'</li>';
if (player.canPlay('spotify')) {
html += '<li><div class="containerbox menuitem infoclick clickaddtolistenlater" style="padding-left:0px">'+
'<div class="fixed alignmid">'+
'<i class="icon-headphones smallicon"></i></div>'+
'<div class="expand">'+language.gettext("label_addtolistenlater")+'</div>'+
'</div></li>';
html += '<li><div class="containerbox menuitem infoclick clickaddtocollection" style="padding-left:0px">'+
'<div class="fixed alignmid">'+
'<i class="icon-music smallicon"></i></div>'+
'<div class="expand">'+language.gettext("label_addtocollection")+'</div>'+
'</div></li>';
}
html += '</ul>';
html += '</div>';
html += '<div class="stumpy selecotron widermiddle">';
html += spotifyTrackListing(data)+'</div>';
html += '<div class="cleft narrowright">';
if (data.images && data.images[0]) {
html += '<img class="cnotshrinker infoclick clickzoomimage" src="getRemoteImage.php?url='+
data.images[0].url+'" />';
}
html += '</div>';
html += '</div>';
return html;
}
function getArtistHTML(data, parent, artistmeta) {
debug.trace(medebug,"Making Artist Info From",data);
if (data.error) {
return '<h3 align="center">'+data.error+'</h3>';
}
var h = "";
if (artistmeta.spotify.possibilities && artistmeta.spotify.possibilities.length > 1) {
h += '<div class="spotchoices clearfix">'+
'<table><tr><td>'+
'<div class="bleft tleft spotthing"><span class="spotpossname">All possibilities for "'+
artistmeta.spotify.artist.name+'"</span></div>'+
'</td><td>';
for (var i in artistmeta.spotify.possibilities) {
h += '<div class="tleft infoclick bleft ';
if (i == artistmeta.spotify.currentposs) {
h += 'bsel ';
}
h += 'clickchooseposs" name="'+i+'">';
if (artistmeta.spotify.possibilities[i].image) {
h += '<img class="spotpossimg title-menu" src="getRemoteImage.php?url='+
artistmeta.spotify.possibilities[i].image+'" />';
} else {
h += '<img class="spotpossimg title-menu" src="newimages/artist-icon.png" />';
}
h += '<span class="spotpossname">'+artistmeta.spotify.possibilities[i].name+'</span>';
h += '</div>';
}
h += '</td></tr></table>';
h += '</div>';
}
h += '<div class="holdingcell">';
h += '<div class="standout stleft statsbox"><ul><li><b>'+language.gettext("label_pop")+
': </b>'+data.popularity+'</li>';
h += '<li><div class="containerbox menuitem infoclick clickstartsingleradio" style="padding-left:0px">'+
'<div class="fixed alignmid">'+
'<i class="icon-wifi smallicon"></i></div>'+
'<div class="expand">'+language.gettext("label_singleartistradio")+'</div>'+
'</div></li>';
if (player.canPlay('spotify')) {
h += '<li>'+
'<div class="containerbox menuitem infoclick clickstartradio" style="padding-left:0px">'+
'<div class="fixed alignmid">'+
'<i class="icon-wifi smallicon"></i></div>'+
'<div class="expand">'+language.gettext("lastfm_simar")+'</div>'+
'</div></li>';
h += '<li>'+
'<div class="containerbox menuitem infoclick clickstartartistradio" style="padding-left:0px">'+
'<div class="fixed alignmid">'+
'<i class="icon-wifi smallicon"></i></div>'+
'<div class="expand">'+language.gettext('label_radio_recommend',['Artist'])+'</div>'+
'</div></li>';
}
h += '</ul></div>';
if (data.images && data.images[0]) {
h += '<img class="stright standout cshrinker infoclick clickzoomimage" '+
'src="getRemoteImage.php?url='+data.images[0].url+'" />';
}
h += '<div id="artistbio" class="minwidthed"></div>';
h += '</div>';
h += '<div class="containerbox textunderline" id="bumhole"><div class="fixed infoclick clickshowalbums bleft';
if (artistmeta.spotify.showing == "albums") {
h += ' bsel';
}
h += '">'+language.gettext("label_albumsby") + '</div>' +
'<div class="fixed infoclick clickshowartists bleft bmid';
if (artistmeta.spotify.showing == "artists") {
h += ' bsel';
}
h += '">'+language.gettext("lastfm_simar")+'</div>' +
'<div class="fixed"><i id="hibbert" class="svg-square title-menu invisible">'+
'</i></div></div>' +
'<div class="fullwidth masonified2" id="artistalbums"></div>';
return h;
}
function findDisplayPanel(element) {
var c = element;
while (!c.hasClass('nobwobbler')) {
c = c.parent();
}
if (c.hasClass('nobalbum')) {
debug.log(medebug,"Opening Album Panel Via Widget");
$('#artistalbums').spotifyAlbumThing('handleClick', element);
return true;
} else if (c.hasClass('nobartist')) {
debug.log(medebug,"Opening Artist Panel Via Widget");
$('#artistalbums').spotifyArtistThing('handleClick', element);
return true;
} else {
debug.error(medebug,"Click On Unknown Element!",element);
return false;
}
}
return {
getRequirements: function(parent) {
return ["musicbrainz"];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace(medebug, "Creating data collection");
var self = this;
var displaying = false;
if (artistmeta.spotify === undefined) {
artistmeta.spotify = {};
}
if (artistmeta.spotify.showing === undefined) {
artistmeta.spotify.showing = "albums";
}
this.populate = function() {
self.track.populate();
}
this.displayData = function() {
displaying = true;
self.artist.doBrowserUpdate();
self.album.doBrowserUpdate();
self.track.doBrowserUpdate();
}
this.stopDisplaying = function() {
displaying = false;
}
this.handleClick = function(source, element, event) {
debug.trace(medebug,parent.nowplayingindex,source,"is handling a click event");
if (element.hasClass('clickzoomimage')) {
imagePopup.create(element, event, element.attr("src"));
} else if (element.hasClass('clickspotifywidget')) {
findDisplayPanel(element);
} else if (element.hasClass('clickchooseposs')) {
var poss = element.attr("name");
if (poss != artistmeta.spotify.currentposs) {
artistmeta.spotify = {
currentposs: poss,
possibilities: artistmeta.spotify.possibilities,
id: artistmeta.spotify.possibilities[poss].id,
showing: "albums"
}
self.artist.force = true;
self.artist.populate();
}
} else if (element.hasClass('clickshowalbums') && artistmeta.spotify.showing != "albums") {
$('#artistalbums').spotifyArtistThing('destroy');
artistmeta.spotify.showing = "albums";
$("#bumhole .bsel").removeClass("bsel");
element.addClass("bsel");
getAlbums();
} else if (element.hasClass('clickshowartists') && artistmeta.spotify.showing != "artists") {
$('#artistalbums').spotifyAlbumThing('destroy');
artistmeta.spotify.showing = "artists";
$("#bumhole .bsel").removeClass("bsel");
element.addClass("bsel");
getArtists();
} else if (element.hasClass('clickaddtolistenlater')) {
metaHandlers.addToListenLater(albummeta.spotify.album);
} else if (element.hasClass('clickaddtocollection')) {
metaHandlers.fromSpotifyData.addAlbumTracksToCollection(albummeta.spotify.album, artistmeta.spotify.artist.name);
} else if (element.hasClass('clickstartradio')) {
playlist.radioManager.load("artistRadio", 'spotify:artist:'+artistmeta.spotify.id);
} else if (element.hasClass('clickstartsingleradio')) {
playlist.radioManager.load("singleArtistRadio", artistmeta.name);
} else if (element.hasClass('clickstarttrackradio')) {
debug.log("SPOTIFY","Starting Track Recommendations With",trackmeta.spotify.id);
playlist.radioManager.load("spotiTrackRadio", {seed_tracks: trackmeta.spotify.id, name: trackmeta.spotify.track.name});
} else if (element.hasClass('clickstartartistradio')) {
debug.log("SPOTIFY","Starting Artist Recommendations With",artistmeta.spotify.id);
playlist.radioManager.load("spotiTrackRadio", {seed_artists: artistmeta.spotify.id, name: artistmeta.spotify.artist.name});
}
}
function getAlbums() {
$("#hibbert").makeSpinner();
if (artistmeta.spotify.albums === undefined) {
spotify.artist.getAlbums(artistmeta.spotify.id, 'album,single', storeAlbums, self.artist.spotifyError, true)
} else {
doAlbums(artistmeta.spotify.albums);
}
}
function getArtists() {
$("#hibbert").makeSpinner()
if (artistmeta.spotify.related === undefined) {
spotify.artist.getRelatedArtists(artistmeta.spotify.id, storeArtists, self.artist.spotifyError, true)
} else {
doArtists(artistmeta.spotify.related);
}
}
function storeAlbums(data) {
artistmeta.spotify.albums = data;
doAlbums(data);
}
function storeArtists(data) {
artistmeta.spotify.related = data;
doArtists(data);
}
function doAlbums(data) {
debug.trace(medebug,"DoAlbums",artistmeta.spotify.showing, displaying);
if (artistmeta.spotify.showing == "albums" && displaying && data) {
debug.trace(medebug,"Doing Albums For Artist",data);
$('#artistalbums').spotifyAlbumThing({
classes: 'nobwobbler nobalbum tagholder2 selecotron',
itemselector: 'nobwobbler',
sub: null,
showbiogs: false,
layoutcallback: function() { $("#hibbert").stopSpinner(); browser.rePoint() },
maxwidth: maxwidth,
is_plugin: false,
imageclass: 'masochist',
data: data.items
});
}
}
function doArtists(data) {
if (artistmeta.spotify.showing == "artists" && displaying && data) {
debug.trace(medebug,"Doing Related Artists",data);
$('#artistalbums').spotifyArtistThing({
classes: 'nobwobbler nobartist tagholder2',
itemselector: 'nobwobbler',
sub: null,
layoutcallback: function() { $("#hibbert").stopSpinner(); browser.rePoint() },
is_plugin: false,
imageclass: 'jalopy',
maxalbumwidth: maxwidth,
data: data.artists
});
}
}
this.track = function() {
function spotifyResponse(data) {
debug.trace(medebug, "Got Spotify Track Data");
debug.trace(medebug, data);
if (trackmeta.spotify.track === undefined) {
trackmeta.spotify.track = data;
}
if (albummeta.spotify === undefined) {
albummeta.spotify = {id: data.album.id};
}
for(var i in data.artists) {
if (data.artists[i].name == artistmeta.name) {
debug.trace(medebug,parent.nowplayingindex,"Found Spotify ID for", artistmeta.name);
artistmeta.spotify.id = data.artists[i].id;
}
}
debug.trace(medebug,"Spotify Data now looks like",artistmeta, albummeta, trackmeta);
self.track.doBrowserUpdate();
self.artist.populate();
}
function gotTrackRecommendations(data) {
trackmeta.spotify.recommendations = data;
doRecommendations(data);
}
function doRecommendations(data) {
$('#helpful_title').html('<div class="textunderline notthere"><h3>'+language.gettext('discover_now', [trackmeta.spotify.track.name])+'</h3></div>');
for (var i in data.tracks) {
var x = $('<div>', {class: 'arsecandle tagholder4 clickable draggable clicktrack playable notthere', name: data.tracks[i].uri}).appendTo($('#helpful_tracks'));
var a = data.tracks[i].album;
var img = '';
if (a.images && a.images[0]) {
img = 'getRemoteImage.php?url='+a.images[0].url
for (var j in a.images) {
if (a.images[j].width <= maxwidth) {
img = 'getRemoteImage.php?url='+a.images[j].url;
break;
}
}
img += '&rompr_resize_size=smallish';
} else {
img = 'newimages/spotify-icon.png';
}
x.append('<img class="cheeseandfish" src="'+img+'" /></div>');
var an = new Array();
for (var j in data.tracks[i].artists) {
an.push(data.tracks[i].artists[j].name);
}
x.append('<div>'+concatenate_artist_names(an)+'<br />'+data.tracks[i].name+'</div>');
}
$('#helpful_tracks').imagesLoaded(doBlockLayout);
}
function doBlockLayout() {
debug.shout(medebug,"Track Images Have Loaded");
browser.rePoint($('#helpful_tracks'),{ itemSelector: '.arsecandle', columnWidth: '.arsecandle', percentPosition: true});
donetheother = true;
setDraggable('#helpful_tracks');
$('#helpful_title').find('.notthere').removeClass('notthere');
$('#helpful_tracks').find('.notthere').removeClass('notthere');
}
return {
populate: function() {
if (trackmeta.spotify === undefined || artistmeta.spotify.id === undefined) {
if (parent.playlistinfo.file.substring(0,8) !== 'spotify:') {
self.track.doBrowserUpdate()
self.artist.populate();
self.album.populate();
} else {
if (trackmeta.spotify === undefined) {
trackmeta.spotify = {id: parent.playlistinfo.file.substr(14, parent.playlistinfo.file.length) };
}
spotify.track.getInfo(trackmeta.spotify.id, spotifyResponse, self.track.spotifyError, true);
}
} else {
self.artist.populate();
}
},
spotifyError: function(data) {
debug.warn(medebug, "Spotify Error!", data);
data.name = parent.playlistinfo.Title;
data.external_urls = {spotify: ''};
trackmeta.spotify.track = data;
if (albummeta.spotify === undefined) {
albummeta.spotify = {};
}
self.track.doBrowserUpdate()
self.artist.populate();
},
spotifyRecError: function(data) {
debug.warn(medebug,"Error getting track reccomendations",data);
},
doBrowserUpdate: function() {
var accepted = false;
if (displaying && trackmeta.spotify !== undefined && trackmeta.spotify.track !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"track was asked to display");
accepted = browser.Update(
null,
'track',
me,
parent.nowplayingindex,
{ name: trackmeta.spotify.track.name,
link: trackmeta.spotify.track.external_urls.spotify,
data: getTrackHTML(trackmeta.spotify.track)
}
);
} else if (parent.playlistinfo.file.substring(0,8) !== 'spotify:') {
browser.Update(null, 'track', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
}
if (accepted && !trackmeta.spotify.track.error) {
if (trackmeta.spotify.recommendations) {
doRecommendations(trackmeta.spotify.recommendations);
} else {
var params = { limit: 8 }
if (prefs.lastfm_country_code) {
params.market = prefs.lastfm_country_code;
}
params.seed_tracks = trackmeta.spotify.id;
spotify.recommendations.getRecommendations(params, gotTrackRecommendations, self.track.spotifyRecError);
}
}
}
}
}();
this.album = function() {
function spotifyResponse(data) {
debug.trace(medebug, "Got Spotify Album Data");
debug.trace(medebug, data);
albummeta.spotify.album = data;
self.album.doBrowserUpdate();
}
return {
populate: function() {
if (albummeta.spotify === undefined || albummeta.spotify.album === undefined ) {
if (parent.playlistinfo.file.substring(0,8) !== 'spotify:') {
self.album.doBrowserUpdate();
} else {
spotify.album.getInfo(albummeta.spotify.id, spotifyResponse, self.album.spotifyError, true);
}
}
},
spotifyError: function(data) {
debug.error(medebug, "Spotify Error!");
data.name = parent.playlistinfo.Album;
data.external_urls = {spotify: ''};
albummeta.spotify.album = data;
self.album.doBrowserUpdate();
},
doBrowserUpdate: function() {
if (displaying && albummeta.spotify !== undefined && albummeta.spotify.album !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"album was asked to display");
var accepted = browser.Update(
null,
'album',
me,
parent.nowplayingindex,
{ name: albummeta.spotify.album.name,
link: albummeta.spotify.album.external_urls.spotify,
data: getAlbumHTML(albummeta.spotify.album)
}
);
} else if (parent.playlistinfo.file.substring(0,8) !== 'spotify:') {
browser.Update(null, 'album', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
}
infobar.markCurrentTrack();
}
}
}();
this.artist = function() {
var triedWithoutBrackets = 0;
var retries = 10;
var searchingfor = artistmeta.name;
function spotifyResponse(data) {
debug.trace(medebug, "Got Spotify Artist Data");
debug.trace(medebug, data);
artistmeta.spotify.artist = data;
self.artist.doBrowserUpdate();
self.album.populate();
}
function search(aname) {
if (parent.playlistinfo.type == "stream" && artistmeta.name == "" && trackmeta.name == "") {
debug.shout(medebug, "Searching Spotify for artist",albummeta.name)
spotify.artist.search(albummeta.name, searchResponse, searchFail, true);
} else {
debug.shout(medebug, "Searching Spotify for artist",aname)
spotify.artist.search(aname, searchResponse, searchFail, true);
}
}
function searchResponse(data) {
debug.trace(medebug,"Got Spotify Search Data",data);
var match = searchingfor.toLowerCase();
artistmeta.spotify.possibilities = new Array();
for (var i in data.artists.items) {
if (data.artists.items[i].name.toLowerCase() == match) {
artistmeta.spotify.possibilities.push({
name: data.artists.items[i].name,
id: data.artists.items[i].id,
image: (data.artists.items[i].images &&
data.artists.items[i].images.length > 0) ?
data.artists.items[i].images[data.artists.items[i].images.length-1].url : null
});
}
}
if (artistmeta.spotify.possibilities.length == 0 && data.artists.items.length == 1) {
// only one match returned, it wasn't an exact match, but use it anyway
artistmeta.spotify.possibilities.push({
name: data.artists.items[0].name,
id: data.artists.items[0].id,
image: (data.artists.items[0].images &&
data.artists.items[0].images.length > 0) ?
data.artists.items[0].images[data.artists.items[0].images.length-1].url : null
});
}
if (artistmeta.spotify.possibilities.length > 0) {
artistmeta.spotify.currentposs = 0;
artistmeta.spotify.id = artistmeta.spotify.possibilities[0].id;
artistmeta.spotify.showing = "albums";
}
if (artistmeta.spotify.id === undefined) {
searchFail();
} else {
self.artist.populate();
}
}
function searchFail() {
debug.trace("SPOTIFY PLUGIN","Couldn't find anything for",artistmeta.name);
var test;
switch (triedWithoutBrackets) {
case 0:
triedWithoutBrackets = 1;
test = artistmeta.name.replace(/ \(+.+?\)+$/, '');
if (test != artistmeta.name) {
searchingfor = test;
debug.trace("SPOTIFY PLUGIN","Searching instead for",test);
search(test);
return;
}
// Fall Through
case 1:
triedWithoutBrackets = 2;
test = artistmeta.name.split(/ & | and /)[0];
if (test != artistmeta.name) {
searchingfor = test;
debug.trace("SPOTIFY PLUGIN","Searching instead for",test);
search(test);
return;
}
// Fall Through
default:
artistmeta.spotify = { artist: { error: '<h3 align="center">'+
language.gettext("label_noartistinfo")+
'</h3>',
name: artistmeta.name,
external_urls: { spotify: '' }
}
};
self.artist.doBrowserUpdate();
break;
}
}
return {
force: false,
populate: function() {
if (artistmeta.spotify.id === undefined) {
search(artistmeta.name);
} else {
if (artistmeta.spotify.artist === undefined) {
spotify.artist.getInfo(artistmeta.spotify.id, spotifyResponse, self.artist.spotifyError, true);
} else {
self.album.populate();
}
}
},
spotifyError: function(data) {
debug.error(medebug, "Spotify Error!");
data.external_urls = {spotify: ''};
data.name = artistmeta.name;
artistmeta.spotify.artist = data;
self.artist.doBrowserUpdate();
self.album.populate();
},
tryForAllmusicBio: function() {
if (typeof artistmeta.allmusic == 'undefined' || typeof artistmeta.allmusic.artistlink === 'undefined') {
debug.shout(medebug,"Allmusic artist link not back yet");
retries--;
if (retries > 0) {
setTimeout(self.artist.tryForAllmusicBio, 2000);
} else {
debug.shout(medebug,"Artist giving up waiting for musicbrainz");
}
} else if (artistmeta.allmusic.artistlink === null) {
debug.shout(medebug,"No Allmusic artist bio link found");
} else {
debug.shout(medebug,"Getting allmusic bio from",artistmeta.allmusic.artistlink);
$.post('browser/backends/getambio.php', {url: artistmeta.allmusic.artistlink})
.done( function(data) {
debug.log(medebug,"Got Allmusic Bio");
if (displaying) $("#artistbio").html(data);
})
.fail( function() {
debug.log(medebug,"Didn't Get Allmusic Bio");
if (displaying) $("#artistbio").html("");
});
}
},
doBrowserUpdate: function() {
if (displaying && artistmeta.spotify !== undefined && artistmeta.spotify.artist !== undefined) {
debug.trace(medebug,parent.nowplayingindex,"artist was asked to display");
var accepted = browser.Update(
null,
'artist',
me,
parent.nowplayingindex,
{ name: artistmeta.spotify.artist.name,
link: artistmeta.spotify.artist.external_urls.spotify,
data: getArtistHTML(artistmeta.spotify.artist, parent, artistmeta)
},
false,
self.artist.force
);
if (accepted && artistmeta.spotify.artist.error == undefined) {
debug.trace(medebug,"Update was accepted by browser");
self.artist.tryForAllmusicBio();
if (artistmeta.spotify.showing == "albums") {
getAlbums();
} else {
getArtists();
}
}
}
}
}
}();
}
}
}();
nowplaying.registerPlugin("spotify", info_spotify, "icon-spotify-circled", "button_infospotify");

View File

@ -0,0 +1,125 @@
var info_videos = function() {
var me = "videos";
return {
getRequirements: function(parent) {
return ['discogs'];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.trace("VIDEOS PLUGIN", "Creating data collection");
var self = this;
var displaying = false;
var ids = [];
var retrytimer;
function mungeDiscogsData(videos) {
debug.trace("VIDEOS PLUGIN","Doing Videos From Discogs Data",videos);
for (var i in videos) {
var u = videos[i].uri;
if (videos[i].embed == true && u.match(/youtube\.com/)) {
var d = u.match(/\/\/www\.youtube\.com\/watch\?v=(.+)$/);
if (d && d[1] && ids.indexOf(d[1]) == -1) {
ids.push(d[1]);
}
}
}
}
function doVideos() {
var html = '';
if (ids.length == 0) {
return '<h3 align="center">No Videos Found</h3>';
}
for (var i in ids) {
html += '<div class="video"><iframe class="youtubevid" src="http://www.youtube.com/embed/'+ids[i]+'"></iframe></div>';
}
return html;
}
function getVideosHtml() {
if (albummeta.discogs.album.master && parent.playlistinfo.type != 'stream') {
debug.debug('VIDEOS', 'Doing videos from album master');
mungeDiscogsData(albummeta.discogs.album.master.data.videos);
}
if (albummeta.discogs.album.release && parent.playlistinfo.type != 'stream') {
debug.debug('VIDEOS', 'Doing videos from album release');
mungeDiscogsData(albummeta.discogs.album.release.data.videos);
}
if (trackmeta.discogs.track.master) {
debug.debug('VIDEOS', 'Doing videos from track master');
mungeDiscogsData(trackmeta.discogs.track.master.data.videos);
}
if (trackmeta.discogs.track.release) {
debug.debug('VIDEOS', 'Doing videos from track release');
mungeDiscogsData(trackmeta.discogs.track.release.data.videos);
}
return doVideos();
}
this.populate = function() {
self.track.populate();
}
this.displayData = function() {
displaying = true;
self.track.doBrowserUpdate();
browser.Update(null, 'album', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
browser.Update(null, 'artist', me, parent.nowplayingindex, { name: "",
link: "",
data: null
}
);
}
this.stopDisplaying = function() {
displaying = false;
clearTimeout(retrytimer);
}
this.track = function() {
return {
populate: function() {
debug.trace('VIDEOS','album master', albummeta.discogs.album.master);
debug.trace('VIDEOS','track master', trackmeta.discogs.track.master);
debug.trace('VIDEOS','album error', albummeta.discogs.album.error);
debug.trace('VIDEOS','track error', trackmeta.discogs.track.error);
if ((trackmeta.discogs.track.master && albummeta.discogs.album.master) ||
(trackmeta.discogs.track.master && albummeta.discogs.album.error) ||
(trackmeta.discogs.track.error && albummeta.discogs.album.master) ||
(trackmeta.discogs.track.error && albummeta.discogs.album.error)) {
self.track.doBrowserUpdate();
} else {
debug.trace("VIDEOS PLUGIN",parent.nowplayingindex,"No data yet, trying again in 1 second");
retrytimer = setTimeout(self.track.populate, 2000);
}
},
doBrowserUpdate: function() {
if (displaying && albummeta.discogs.album !== undefined && trackmeta.discogs.track !== undefined &&
(albummeta.discogs.album.error !== undefined || albummeta.discogs.album.master !== undefined) &&
(trackmeta.discogs.track.error !== undefined || trackmeta.discogs.track.master !== undefined)) {
debug.mark("VIDEOS PLUGIN",parent.nowplayingindex,"track was asked to display");
browser.Update(null, 'track', me, parent.nowplayingindex, { name: artistmeta.name+' / '+trackmeta.name,
link: "",
data: getVideosHtml()
}
);
}
}
}
}();
}
}
}();
nowplaying.registerPlugin("videos", info_videos, "icon-video", "button_videos");

View File

@ -0,0 +1,189 @@
var info_ratings = function() {
var me = "ratings";
var trackFinder = new faveFinder(false);
var update_wishlist = false;
return {
getRequirements: function(parent) {
return [];
},
collection: function(parent, artistmeta, albummeta, trackmeta) {
debug.log("RATINGS PLUGIN", "Creating data collection");
var self = this;
var displaying = false;
var lfmupdates = null;
function doThingsWithData() {
if (parent.isCurrentTrack() && trackmeta.usermeta) {
if (prefs.sync_lastfm_playcounts && lfmupdates !== null) {
$.each(lfmupdates, function(i, v) {
switch (i) {
case 'Playcount':
if (parseInt(trackmeta.usermeta[i]) < parseInt(v)) {
debug.log("RATINGS PLUGIN","Update :",i,"is now",v);
trackmeta.usermeta[i] = v;
} else {
debug.log("RATINGS PLUGIN","Not using update for",i,"as",v,"is less than",trackmeta.usermeta[i]);
}
break;
}
});
}
var playtext = '';
if (trackmeta.usermeta.Playcount && trackmeta.usermeta.Playcount > 0) {
playtext = '<span class="playspan"><b>PLAYS </b>&nbsp;'+trackmeta.usermeta.Playcount+'</span>';
if (uiHelper.showTagButton()) {
$("#playcount").html(playtext);
}
if (typeof charts != 'undefined') {
charts.reloadAll();
}
if (typeof recentlyPlayed != 'undefined') {
recentlyPlayed.reloadAll();
}
} else {
$("#playcount").empty();
}
displayRating("#ratingimage", trackmeta.usermeta.Rating);
if (uiHelper.showTagButton()) {
$("#dbtags").html('<span><b>'+language.gettext("musicbrainz_tags")+
'</b></span><i class="icon-plus clickicon playlisticon" '+
'onclick="tagAdder.show(event)" style="margin-left:2px;margin-top:0px;margin-right:1em;"></i>');
} else {
$('#dbtags').html(playtext);
if (trackmeta.usermeta.Tags.length > 0) {
debug.log('INFOBAR', 'Tags are',trackmeta.usermeta.Tags.length);
$('#dbtags').append('<span><b>'+language.gettext("musicbrainz_tags")+' &nbsp;</b></span>');
}
}
for(var i = 0; i < trackmeta.usermeta.Tags.length; i++) {
$("#dbtags").append('<span class="tag">'+trackmeta.usermeta.Tags[i]+
'<i class="icon-cancel-circled clickicon tagremover playlisticon" style="display:none"></i></span> ');
}
layoutProcessor.adjustLayout();
}
// Make sure the browser updates the file info display
browser.reDo(parent.nowplayingindex, 'file');
}
function hideTheInputs() {
if (parent.isCurrentTrack()) {
displayRating("#ratingimage", false);
$("#dbtags").html('');
$("#playcount").html('');
}
}
function setSuccess(rdata) {
debug.log("RATING PLUGIN","Success");
if (rdata) {
trackmeta.usermeta = rdata.metadata;
doThingsWithData();
collectionHelper.updateCollectionDisplay(rdata);
}
}
function findSuccess(rdata) {
debug.log("RATING PLUGIN","Success");
if (rdata) {
trackmeta.usermeta = rdata.metadata;
doThingsWithData();
collectionHelper.updateCollectionDisplay(rdata);
if (!rdata.hasOwnProperty('addedtracks')) {
infobar.error(language.gettext('error_trackexists'));
}
}
if (update_wishlist && typeof(wishlistViewer) != 'undefined') {
wishlistViewer.update();
}
update_wishlist = false;
}
function setFail(rdata) {
debug.warn("RATING PLUGIN","Failure");
// infobar.error("Failed! Have you read the Wiki?");
doThingsWithData();
}
this.displayData = function() {
debug.error("RATINGS PLUGIN", "Was asked to display data!");
}
this.stopDisplaying = function() {
}
this.updateMeta = function(updates) {
lfmupdates = updates;
doThingsWithData();
}
this.refresh = function() {
trackmeta.usermeta = undefined;
self.populate();
}
this.populate = function() {
if (trackmeta.usermeta === undefined) {
metaHandlers.fromPlaylistInfo.getMeta(
parent.playlistinfo,
function(data) {
trackmeta.usermeta = data;
doThingsWithData();
},
function(data) {
trackmeta.usermeta = null;
hideTheInputs();
}
);
} else {
debug.mark("RATINGS PLUGIN",parent.nowplayingindex,"is already populated");
doThingsWithData();
}
}
this.setMeta = function(action, type, value) {
debug.log("RATINGS PLUGIN",parent.nowplayingindex,"Doing",action,type,value);
if (parent.playlistinfo.type == 'stream') {
infobar.notify(language.gettext('label_searching'));
trackFinder.findThisOne(metaHandlers.fromPlaylistInfo.mapData(parent.playlistinfo, action, [{attribute: type, value: value}]),
self.updateDatabase
);
} else {
metaHandlers.fromPlaylistInfo.setMeta(parent.playlistinfo, action, [{attribute: type, value: value}], setSuccess, setFail);
}
}
this.setAlbumMBID = function(mbid) {
debug.log("RATINGS PLUGIN",parent.nowplayingindex," Updating backend album MBID");
metaHandlers.fromPlaylistInfo.setMeta(parent.playlistinfo, 'setalbummbid', mbid, false, false);
}
this.getMeta = function(meta) {
if (trackmeta.usermeta) {
if (trackmeta.usermeta[meta]) {
return trackmeta.usermeta[meta];
} else {
return 0;
}
} else {
return 0;
}
}
this.updateDatabase = function(data) {
debug.log("RATINGS","Update Database Function Called",data);
if (!data.uri) {
infobar.notify(language.gettext("label_addtow"));
update_wishlist = true;
}
dbQueue.request([data], findSuccess, setFail);
}
}
}
}();
nowplaying.registerPlugin("ratings", info_ratings, null, null);

View File

@ -0,0 +1,26 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>RompЯ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<script type="text/javascript" src="jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="ui/functions.js"></script>
<script language="javascript">
$(document).ready(function() {
var ws = getWindowSize();
if ((ws.x < 600 && ws.x < ws.y) || (ws.x < 800 && ws.x > ws.y)) {
setCookie('skin','phone',3650);
} else if ((ws.x < 1024 && ws.x < ws.y) || (ws.x < 600 && ws.x > ws.y)) {
setCookie('skin','tablet',3650);
} else {
setCookie('skin','desktop',3650);
}
location.reload(true);
});
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,362 @@
<?php
require_once ("includes/spotifyauth.php");
$numtracks = 0;
$numalbums = 0;
$numartists = 0;
$totaltime = 0;
$count = 1;
$divtype = "album1";
$dbterms = array( 'tags' => null, 'rating' => null );
$trackbytrack = false;
define('ROMPR_MIN_TRACKS_TO_DETERMINE_COMPILATION', 3);
define('ROMPR_MIN_NOT_COMPILATION_THRESHOLD', 0.6);
class musicCollection {
public function __construct() {
$this->albums = array();
$this->filter_duplicates = false;
}
public function newTrack(&$filedata) {
global $trackbytrack, $doing_search;
if ($doing_search) {
// If we're doing a search, we check to see if that track is in the database
// because the user might have set the AlbumArtist to something different
$filedata = array_replace($filedata, get_extra_track_info($filedata));
}
$track = new track($filedata);
if ($trackbytrack && $filedata['AlbumArtist'] && $filedata['Disc'] !== null) {
do_track_by_track( $track );
} else {
$albumkey = md5($track->tags['folder'].strtolower($track->tags['Album']).strtolower($track->get_sort_artist(true)));
if (array_key_exists($albumkey, $this->albums)) {
if (!$this->filter_duplicates || !$this->albums[$albumkey]->checkForDuplicate($track)) {
$this->albums[$albumkey]->newTrack($track);
}
} else {
$this->albums[$albumkey] = new album($track);
}
}
}
public function getAllTracks($cmd) {
$tracks = array();
foreach($this->albums as $album) {
$tracks = array_merge($album->getAllTracks($cmd), $tracks);
}
return $tracks;
}
public function tracks_to_database() {
// Fluch the previous albumobj from track_by_track
do_track_by_track(null);
foreach ($this->albums as $album) {
$album->sortTracks();
$album->check_database();
}
$this->albums = array();
}
public function get_albumartist_by_folder($f) {
foreach ($this->albums as $album) {
if ($album->folder == $f) {
logger::trace("COLLECTION", " Found albumartist by folder",$album->artist);
return $album->artist;
}
}
return null;
}
public function filter_duplicate_tracks() {
$this->filter_duplicates = true;
}
public function tracks_as_array() {
$results = array();
foreach($this->albums as $album) {
logger::log("COLLECTION", "Doing Album",$album->name);
$album->sortTracks();
foreach($album->tracks as $trackobj) {
$track = array(
"uri" => $trackobj->tags['file'],
"album" => $album->name,
"title" => $trackobj->tags['Title'],
"artist" => $trackobj->get_artist_string(),
"albumartist" => $album->artist,
"trackno" => $trackobj->tags['Track'],
"disc" => $trackobj->tags['Disc'],
"albumuri" => $album->uri,
"image" => $album->getImage('asdownloaded'),
"duration" => $trackobj->tags['Time'],
"date" => $album->datestamp
);
logger::log("COLLECTION", "Title - ".$trackobj->tags['Title']);
// A lot of code that depends on this was written to handle mopidy model search results.
// The above is not mopidy model, so friggicate it into just such a thing
$d = getDomain($track['uri']);
if (!array_key_exists($d, $results)) {
logger::log("COLLECTION", "Creating Results Set For ".$d);
$results[$d] = array(
"tracks" => array(),
"uri" => $d.':bodgehack'
);
}
array_push($results[$d]['tracks'], $track);
}
}
return $results;
}
}
class album {
public function __construct(&$track) {
global $numalbums;
$numalbums++;
$this->tracks = array($track);
// Sets album artist to composer (if set and required) or albumartist but NOT trackartist
// therefore may still be null at this point.
$this->artist = $track->get_sort_artist(true);
$this->name = trim($track->tags['Album']);
$this->folder = $track->tags['folder'];
$this->musicbrainz_albumid = $track->tags['MUSICBRAINZ_ALBUMID'];
$this->datestamp = $track->tags['Date'];
$this->uri = $track->tags['X-AlbumUri'];
$this->numOfDiscs = $track->tags['Disc'];
$this->image = $track->tags['X-AlbumImage'];
$this->key = $track->tags['ImgKey'];
$this->numOfDiscs = $track->tags['Disc'];
$this->numOfTrackOnes = $track->tags['Track'] == 1 ? 1 : 0;
$this->domain = $track->tags['domain'];
$this->albumartistindex = null;
$this->albumindex = null;
}
public function newTrack(&$track) {
$this->tracks[] = $track;
if ($this->artist == null) {
$this->artist = $track->get_sort_artist(true);
}
if ($this->image == null) {
$this->image = $track->tags['X-AlbumImage'];
}
if ($this->datestamp == null) {
$this->datestamp = $track->tags['Date'];
}
if ($this->musicbrainz_albumid == '') {
$this->musicbrainz_albumid = $track->tags['MUSICBRAINZ_ALBUMID'];
}
if ($track->tags['Disc'] !== null && $this->numOfDiscs < $track->tags['Disc']) {
$this->numOfDiscs = $track->tags['Disc'];
}
if ($track->tags['Track'] == 1) {
$this->numOfTrackOnes++;
}
if ($this->uri == null) {
$this->uri = $track->tags['X-AlbumUri'];
}
}
public function check_database() {
if ($this->albumartistindex == null) {
$this->albumartistindex = check_artist($this->artist);
}
if ($this->albumindex == null) {
$album = array(
'album' => $this->name,
'albumai' => $this->albumartistindex,
'albumuri' => $this->uri,
'image' => $this->getImage('small'),
'date' => $this->getDate(),
'searched' => "0",
'imagekey' => $this->getKey(),
'ambid' => $this->musicbrainz_albumid,
'domain' => $this->domain
);
$this->albumindex = check_album($album);
}
foreach ($this->tracks as $trackobj) {
check_and_update_track($trackobj, $this->albumindex, $this->albumartistindex, $this->artist);
}
}
public function getKey() {
return $this->key;
}
public function getImage($size) {
$albumimage = new baseAlbumImage(array(
'baseimage' => ($this->image) ? $this->image : '',
'artist' => artist_for_image($this->tracks[0]->tags['type'], $this->artist),
'album' => $this->name
));
$albumimage->check_image($this->domain, $this->tracks[0]->tags['type']);
$images = $albumimage->get_images();
$this->key = $albumimage->get_image_key();
return $images[$size];
}
public function trackCount() {
return count($this->tracks);
}
public function getDate() {
return getYear($this->datestamp);
}
public function getAllTracks($cmd) {
$tracks = array();
foreach ($this->tracks as $track) {
if (preg_match('/:track:/', $track->tags['file'])) {
$tracks[] = $cmd.' "'.format_for_mpd($track->tags['file']).'"';
}
}
return $tracks;
}
public function sortTracks($always = false) {
// NB. BLOODY WELL CALL THIS FUNCTION
// Unless you're so sure you know how all this works and you really don't need it.
// Collection updates might be one such area but if you're not sure CALL IT ANYWAY and see what happens.
// Some Mopidy backends don't send disc numbers. If we're using the sql backend
// we don't really need to pre-sort tracks because we can do it on the fly.
// However, when there are no disc numbers multi-disc albums don't sort properly.
// Hence we do a little check that we have have the same number of 'Track 1's
// as discs and only do the sort if they're not the same. This'll also
// sort out badly tagged local files. It's essential that disc numbers are set
// because the database will not find the tracks otherwise.
// Also here, because ths gets called always, we try to set our albumartist
// to something sensible. So far it has been set to Composer tags if required by the
// user, or to the AlbumArtist setting, which will be null if no AlbumArtist tag is present -
// as is the case with many mopidy backends
if ($this->artist == null) {
logger::mark("COLLECTION", "Finding AlbumArtist for album ".$this->name);
if (count($this->tracks) < ROMPR_MIN_TRACKS_TO_DETERMINE_COMPILATION) {
logger::log("COLLECTION", " Album ".$this->name." has too few tracks to determine album artist");
$this->decideOnArtist($this->tracks[0]->get_sort_artist());
} else {
$artists = array();
foreach ($this->tracks as $track) {
$a = $track->get_sort_artist();
if (!array_key_exists($a, $artists)) {
$artists[$a] = 0;
}
$artists[$a]++;
}
$q = array_flip($artists);
rsort($q);
$candidate_artist = $q[0];
$fraction = $artists[$candidate_artist]/count($this->tracks);
logger::log("COLLECTION", " Artist ".$candidate_artist." has ".$artists[$candidate_artist]." tracks out of ".count($this->tracks));
if ($fraction > ROMPR_MIN_NOT_COMPILATION_THRESHOLD) {
logger::log("COLLECTION", " ... which is good enough. Album ".$this->name." is by ".$candidate_artist);
$this->artist = $candidate_artist;
} else {
logger::log("COLLECTION", " ... which is not enough");
$this->decideOnArtist("Various Artists");
}
}
}
foreach ($this->tracks as $track) {
$track->tags['AlbumArtist'] = $this->artist;
}
if ($always == false && $this->numOfDiscs > 0 && ($this->numOfTrackOnes <= 1 || $this->numOfTrackOnes == $this->numOfDiscs)) {
return $this->numOfDiscs;
}
$discs = array();
$number = 1;
foreach ($this->tracks as $ob) {
if ($ob->tags['Track'] !== '') {
$track_no = intval($ob->tags['Track']);
} else {
$track_no = $number;
}
# Just in case we have a multiple disc album with no disc number tags
$discno = intval($ob->tags['Disc']);
if ($discno == '' || $discno == null || $discno == 0) {
$discno = 1;
}
if (!array_key_exists($discno, $discs)) {
$discs[$discno] = array();
}
while(array_key_exists($track_no, $discs[$discno])) {
$discno++;
if (!array_key_exists($discno, $discs)) {
$discs[$discno] = array();
}
}
$discs[$discno][$track_no] = $ob;
$ob->updateTrackInfo(array('Disc' => $discno));
$number++;
}
$numdiscs = count($discs);
$this->tracks = array();
ksort($discs, SORT_NUMERIC);
foreach ($discs as $disc) {
ksort($disc, SORT_NUMERIC);
$this->tracks = array_merge($this->tracks, $disc);
}
$this->numOfDiscs = $numdiscs;
return $numdiscs;
}
public function checkForDuplicate($t) {
foreach ($this->tracks as $track) {
if ($t->tags['file'] == $track->tags['file']) {
logger::trace("COLLECTION", "Filtering Duplicate Track ".$t->tags['file']);
return true;
}
}
return false;
}
private function decideOnArtist($candidate) {
if ($this->artist == null) {
logger::log("COLLECTION", " ... Setting artist to ".$candidate);
$this->artist = $candidate;
}
}
}
class track {
public $tags;
public function __construct(&$filedata) {
$this->tags = $filedata;
}
public function updateTrackInfo($info) {
$this->tags = array_replace($this->tags, $info);
}
public function get_artist_string() {
return format_artist($this->tags['Artist']);
}
public function get_sort_artist($return_albumartist = false) {
return format_sortartist($this->tags, $return_albumartist);
}
}
?>

View File

@ -0,0 +1,156 @@
<?php
require_once ('player/mpd/filetree.php');
function doDbCollection($terms, $domains, $resultstype, &$collection) {
// This can actually be used to search the database for title, album, artist, anything, rating, and tag
// But it isn't because we let Mopidy/MPD search for anything they support because otherwise we
// have to duplicate their entire database, which is daft.
// This function was written before I realised that... :)
// It's still used for searches where we're only looking for tags and/or ratings in conjunction with
// any of the above terms, because mopidy often returns incomplete search results.
$parameters = array();
$qstring = "SELECT t.*, al.*, a1.*, a2.Artistname AS AlbumArtistName ";
if (array_key_exists('rating', $terms)) {
$qstring .= ",rat.Rating ";
}
$qstring .= "FROM Tracktable AS t ";
if (array_key_exists('tag', $terms)) {
$qstring .= "JOIN (SELECT DISTINCT TTindex FROM TagListtable JOIN Tagtable AS tag USING (Tagindex) WHERE";
$tagterms = array();
foreach ($terms['tag'] as $tag) {
$parameters[] = trim($tag);
$tagterms[] = " tag.Name LIKE ?";
}
$qstring .= implode(" OR",$tagterms);
$qstring .=") AS j ON j.TTindex = t.TTindex ";
}
if (array_key_exists('rating', $terms)) {
$qstring .= "JOIN (SELECT * FROM Ratingtable WHERE Rating >= ".
$terms['rating'].") AS rat ON rat.TTindex = t.TTindex ";
}
$qstring .= "JOIN Artisttable AS a1 ON a1.Artistindex = t.Artistindex ";
$qstring .= "JOIN Albumtable AS al ON al.Albumindex = t.Albumindex ";
$qstring .= "JOIN Artisttable AS a2 ON al.AlbumArtistindex = a2.Artistindex ";
if (array_key_exists('wishlist', $terms)) {
$qstring .= "WHERE t.Uri IS NULL";
} else {
$qstring .= "WHERE t.Uri IS NOT NULL ";
}
$qstring .= "AND t.Hidden = 0 AND t.isSearchResult < 2 ";
// Map search parameters to database tables
$searchmap = array(
'artist' => 'a1.Artistname',
'album' => 'al.Albumname',
'title' => 't.Title',
'file' => 't.Uri',
'albumartist' => 'a2.Artistname'
);
foreach ($searchmap as $t => $d) {
if (array_key_exists($t, $terms)) {
$qstring .= 'AND (';
$qstring .= format_for_search($terms[$t],$d, $parameters);
$qstring .= ' OR '.format_for_search2($terms[$t],$d, $parameters);
$qstring .= ') ';
}
}
if (array_key_exists('any', $terms)) {
$qstring .= ' AND (';
$bunga = array();
foreach ($terms['any'] as $tim) {
$t = explode(' ',$tim);
foreach ($t as $tom) {
foreach ($searchmap AS $d) {
$bunga[] = format_for_search(array($tom), $d, $parameters);
$bunga[] = format_for_search2(array($tom), $d, $parameters);
}
}
}
$qstring .= implode(' OR ', $bunga);
$qstring .= ')';
}
if (array_key_exists('date', $terms)) {
$qstring .= "AND ";
$parameters[] = trim($terms['date'][0]);
$qstring .= "al.Year = ? ";
}
if (is_array($domains)) {
$qstring .= "AND (";
$domainterms = array();
foreach ($domains as $dom) {
$parameters[] = trim($dom)."%";
$domainterms[] = "t.Uri LIKE ?";
}
$qstring .= implode(" OR ",$domainterms);
$qstring .= ")";
}
logger::log("DB SEARCH", "Parameters", $parameters);
$result = sql_prepare_query(false, PDO::FETCH_OBJ, null, null, $qstring, $parameters);
$fcount = count($result);
foreach ($result as $obj) {
$filedata = array(
'Artist' => array($obj->Artistname),
'Album' => $obj->Albumname,
'AlbumArtist' => array($obj->AlbumArtistName),
'file' => $obj->Uri,
'Title' => $obj->Title,
'Track' => $obj->TrackNo,
'X-AlbumImage' => $obj->Image,
'Time' => $obj->Duration,
'X-AlbumUri' => $obj->AlbumUri,
'Date' => $obj->Year,
'Last-Modified' => $obj->LastModified
);
$filedata = array_merge(MPD_FILE_MODEL, $filedata);
logger::log("DB SEARCH", "Found :",$obj->Title,$obj->Uri);
if ($resultstype == "tree") {
$collection->newItem($filedata);
} else if ($resultstype == "RAW") {
$collection->newTrack($filedata);
} else {
generic_sql_query("UPDATE Tracktable SET isSearchResult = 1 WHERE TTindex = ".$obj->TTindex, true);
}
}
return $fcount;
}
function format_for_search($terms, $s, &$parameters) {
// Make things a little more searchable
$a = array();
foreach ($terms as $i => $term) {
$t = trim($term);
$t = preg_replace('/[\(\)\/\[\]\&\*\+\'\"\,\/]/','%',$t);
$a[] = $s.' LIKE ?';
$parameters[] = '% '.$t. '%';
$a[] = $s.' LIKE ?';
$parameters[] = '%'.$t. ' %';
}
$ret = implode(' OR ',$a);
return $ret;
}
function format_for_search2($terms, $s, &$parameters) {
// Make things a little more searchable
$a = array();
foreach ($terms as $i => $term) {
$t = trim($term);
$t = preg_replace('/[\(\)\/\[\]\&\*\+\'\"\,\/]/','%',$t);
$a[] = $s.' = ?';
$parameters[] = $t;
}
$ret = implode(' OR ',$a);
return $ret;
}
?>

View File

@ -0,0 +1,199 @@
<?php
class playlistCollection {
private $foundartists;
public function doNewPlaylistFile(&$filedata) {
global $prefs;
// Translate from MPD_FILE_MODEL to ROMPR_FILE_MODEL
$info = array_replace(ROMPR_FILE_MODEL, $filedata);
if ($info['Title'] === null) {
$info['Title'] = '';
}
$albumartist = format_sortartist($filedata);
// Bloody spotify often returns album artist = A & B but track artists 'B' and 'A'.
// This screws up the playcount stats. They're also not consistent with
// capitalisation of articles in Album Artists
$tartist = format_artist($filedata['Artist'],'');
$tartist_reversed = is_array($filedata['Artist']) ? format_artist(array_reverse($filedata['Artist']),'') : '';
if (strtolower($tartist_reversed) == strtolower($albumartist)) {
$info['Artist'] = array_reverse($info['Artist']);
$tartist = $tartist_reversed;
$albumartist = $tartist_reversed;
}
$albumimage = new baseAlbumImage(array(
'baseimage' => $filedata['X-AlbumImage'],
'artist' => artist_for_image($filedata['type'], $albumartist),
'album' => $filedata['Album']
));
$albumimage->check_image($filedata['domain'], $filedata['type'], true);
$info['Id'] = (int) $filedata['Id'];
$info['ImgKey'] = $albumimage->get_image_key();
$info['images'] = $albumimage->get_images();
$info['trackartist'] = $tartist;
$info['albumartist'] = $albumartist;
$info['metadata']['track']['name'] = trim($filedata['Title']);
$info['metadata']['track']['musicbrainz_id'] = trim($filedata['MUSICBRAINZ_TRACKID']);
$info['metadata']['album']['name'] = trim($filedata['Album']);
$info['metadata']['album']['artist'] = trim($albumartist);
$info['metadata']['album']['musicbrainz_id'] = trim($filedata['MUSICBRAINZ_ALBUMID']);
$info['metadata']['album']['uri'] = $filedata['X-AlbumUri'];
if ($info['X-AlbumUri'] && getDomain($filedata['X-AlbumUri']) == 'spotify') {
$info['metadata']['album']['spotify'] = array(
'id' => substr($filedata['X-AlbumUri'], 14)
);
}
$this->foundartists = array();
// All kinds of places we get artist names from:
// Composer, Performer, Track Artist, Album Artist
// Note that we filter duplicates
// This creates the metadata array used by the info panel and nowplaying -
// Metadata such as scrobbles and ratings will still use the Album Artist
if ($prefs['displaycomposer']) {
// The user has chosen to display Composer/Perfomer information
// Here check:
// a) There is composer/performer information AND
// bi) Specific Genre Selected, Track Has Genre, Genre Matches Specific Genre OR
// bii) No Specific Genre Selected, Track Has Genre
if (($info['Composer'] !== null || $info['Performer'] !== null) &&
(($prefs['composergenre'] && $info['Genre'] &&
checkComposerGenre($info['Genre'], $prefs['composergenrename'])) ||
(!$prefs['composergenre'] && $info['Genre'])))
{
// Track Genre matches selected 'Sort By Composer' Genre
// Display Compoer - Performer - AlbumArtist
$this->do_composers($info);
$this->do_performers($info);
// The album artist probably won't be required in this case, but use it just in case
$this->do_albumartist($info);
// Don't do track artist as with things tagged like this this is usually rubbish
} else {
// Track Genre Does Not Match Selected 'Sort By Composer' Genre
// Or there is no composer/performer info
// Do Track Artist - Album Artist - Composer - Performer
$this->do_track_artists($info);
$this->do_albumartist($info);
$this->do_performers($info);
$this->do_composers($info);
}
if ($info['Composer'] !== null || $info['Performer'] !== null) {
$info['metadata']['iscomposer'] = 'true';
}
} else {
// The user does not want Composer/Performer information
$this->do_track_artists($info, $albumartist);
$this->do_albumartist($info, $albumartist);
}
if (count($info['metadata']['artists']) == 0) {
array_push($info['metadata']['artists'], array( "name" => "", "musicbrainz_id" => ""));
}
return $info;
}
private function do_composers(&$info) {
if ($info['Composer'] == null) {
return;
}
foreach ($info['Composer'] as $comp) {
if ($this->artist_not_found_yet($comp)) {
array_push($info['metadata']['artists'], array( "name" => trim($comp), "musicbrainz_id" => "", "type" => "composer", "ignore" => "false"));
}
}
}
private function do_performers(&$info) {
if ($info['Performer'] == null) {
return;
}
foreach ($info['Performer'] as $comp) {
$toremove = null;
foreach($info['metadata']['artists'] as $i => $artist) {
if ($artist['type'] == "albumartist" || $artist['type'] == "artist") {
if (strtolower($artist['name'] == strtolower($comp))) {
$toremove = $i;
break;
}
}
}
if ($toremove !== null) {
array_splice($info['metadata']['artists'], $toremove, 1);
}
if ($toremove !== null || $this->artist_not_found_yet($comp)) {
array_push($info['metadata']['artists'], array( "name" => trim($comp), "musicbrainz_id" => "", "type" => "performer", "ignore" => "false"));
}
}
}
private function do_albumartist(&$info) {
$aartist = null;
if (!($info['type'] == "stream" && $info['albumartist'] == "Radio") &&
strtolower($info['albumartist']) != "various artists" &&
strtolower($info['albumartist']) != "various")
{
$aartist = $info['albumartist'];
}
if ($aartist !== null && $this->artist_not_found_yet($aartist)) {
array_push($info['metadata']['artists'], array( "name" => trim($aartist), "musicbrainz_id" => trim($info['MUSICBRAINZ_ALBUMARTISTID']), "type" => "albumartist", "ignore" => "false"));
}
}
private function do_track_artists(&$info) {
if ($info['Artist'] == null) {
return;
}
$artists = getArray($info['Artist']);
$mbids = $info['MUSICBRAINZ_ARTISTID'];
if (count($mbids) > count($artists)) {
// More MBIDs that Artists. This might be one of those daft things where MBIDs are semicolon-separated
// but artists are comma-separated.
// You can even get artists = ['artist1, artist2', 'artist3']. Sigh. Hence the first implode.
$astring = implode(', ',$artists);
$newartists = explode(',', $astring);
if (count($newartists) == count($mbids)) {
logger::trace("Trying splitting comma-separated artist string", "GETPLAYLIST");
// In case AlbumArtist has that format too
$this->artist_not_found_yet($astring);
$artists = $newartists;
}
}
while (count($mbids) < count($artists)) {
$mbids[] = "";
}
$a = array();
foreach ($artists as $i => $comp) {
if ($comp != "") {
if ($this->artist_not_found_yet($comp)) {
array_push($info['metadata']['artists'], array( "name" => trim($comp), "musicbrainz_id" => trim($mbids[$i]), "type" => "artist", "ignore" => "false"));
$a[] = $comp;
}
}
}
// This is to try and prevent repeated names - eg artists = [Pete, Dud] and albumartist = Pete & Dud or Dud & Pete
$this->artist_not_found_yet(concatenate_artist_names($a));
$this->artist_not_found_yet(concatenate_artist_names(array_reverse($a)));
}
private function artist_not_found_yet($a) {
$s = strtolower($a);
if (in_array($s, $this->foundartists)) {
return false;
} else {
$this->foundartists[] = $s;
return true;
}
}
}
?>

View File

@ -0,0 +1,13 @@
.smallcover
{
width:24px;
}
.smallcover-svg {
height:24px;
}
img.notexist,img.notfound {
width:24px;
height:24px;
}

View File

@ -0,0 +1,13 @@
.smallcover
{
width:32px;
}
.smallcover-svg {
height:32px;
}
img.notexist,img.notfound {
width:32px;
height:32px;
}

View File

@ -0,0 +1,23 @@
.smallcover
{
width:48px;
}
.smallcover-svg {
height:48px;
}
img.notexist,img.notfound {
width:48px;
height:48px;
}
body.desktop .smallcover.svg-square.noindent
{
width:32px;
}
body.mobile .smallcover.svg-square.noindent
{
width:32px;
}

View File

@ -0,0 +1,24 @@
.smallcover
{
width:64px;
}
.smallcover-svg {
height:64px;
}
img.notexist,img.notfound {
width:64px;
height:64px;
}
body.desktop .smallcover.svg-square.noindent
{
width:42px;
margin-right:18px;
}
body.mobile .smallcover.svg-square.noindent
{
width:32px;
}

View File

@ -0,0 +1,25 @@
.smallcover
{
width:82px;
}
.smallcover-svg {
height:82px;
}
img.notexist,img.notfound {
width:82px;
height:82px;
}
body.desktop .smallcover.svg-square.noindent
{
width:42px;
margin-right:28px;
}
body.mobile .smallcover.svg-square.noindent
{
width:32px;
margin-right:16px;
}

View File

@ -0,0 +1,25 @@
.smallcover
{
width:100px;
}
.smallcover-svg {
height:100px;
}
img.notexist,img.notfound {
width:100px;
height:100px;
}
body.desktop .smallcover.svg-square.noindent
{
width:42px;
margin-right:28px;
}
body.mobile .smallcover.svg-square.noindent
{
width:32px;
margin-right:16px;
}

View File

@ -0,0 +1,148 @@
#imageeditor {
min-height:400px;
box-sizing:border-box;
position: relative;
z-index:100;
}
#origimage {
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
max-width:40%;
border-left: 1px solid #fe6500;
margin-right:8px;
margin-bottom:8px;
padding-right: 8px;
padding-bottom: 8px;
margin-top:8px;
transition: width 0.5s ease;
}
#browns {
transition: opacity 0.5s ease;
opacity: 0;
}
.albumcovers .infosection {
margin-bottom:0px;
}
.gimage {
float:left;
padding:8px;
}
.outer {
width:20%;
}
.closet {
position:relative;
width:160px;
text-align:center;
overflow:visible;
}
.covercontainer
{
position: relative;
padding-left:4px;
padding-right:4px;
z-index:1;
}
.covercontainer div {
z-index:1;
}
.covercontainer.highlighted
{
z-index:150;
box-sizing:border-box;
}
.rightpad {
padding-right:16px;
margin-right:16px;
}
img#browns {
max-width:100%;
}
#searchcontent {
flex-grow: 2;
flex-shrink: 1;
flex-basis: auto;
padding:8px;
}
.albumimg {
padding-top:8px;
}
.albumimg img {
padding:8px;
}
.albumimg .covercontainer img {
width: 100px;
height:auto;
}
.albumimg .covercontainer img.notfound, .albumimg .covercontainer img.notexist {
width: 100px;
height: 100px;
}
#brian {
padding-left:8px;
padding-right:8px;
}
#editcontrols {
padding-top:2px;
padding-bottom:4px;
}
#artistcoverslist {
overflow-y:auto;
height:100%;
width:20%;
}
#coverslist {
overflow-y:auto;
height:100%;
width:80%;
}
#progress {
font-size:12pt;
}
.artistrow {
padding-top:2px;
padding-bottom:2px;
}
.albumsection {
padding-top:4px;
padding-bottom:4px;
display:table;
width:100%;
margin-left:4px;
}
.albumsection .tleft h2 {
margin:8px;
}
.albumsection .tleft button {
margin-top:8px;
}
#morebutton {
text-align:center;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

109
www/jukebox/css/tipTip.css Normal file
View File

@ -0,0 +1,109 @@
/* TipTip CSS - Version 1.2 */
#tiptip_holder {
display: none;
position: absolute;
top: 0;
left: 0;
z-index: 99999;
}
#tiptip_holder.tip_top {
padding-bottom: 5px;
}
#tiptip_holder.tip_bottom {
padding-top: 5px;
}
#tiptip_holder.tip_right {
padding-left: 5px;
}
#tiptip_holder.tip_left {
padding-right: 5px;
}
#tiptip_content {
font-size: 11px;
color: #fff;
text-shadow: 0 0 2px #000;
padding: 4px 8px;
border: 1px solid rgba(255,255,255,0.25);
background-color: rgb(25,25,25);
background-color: rgba(25,25,25,0.92);
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(#000));
border-radius: 3px;
box-shadow: 0 0 3px #555;
}
#tiptip_arrow, #tiptip_arrow_inner {
position: absolute;
border-color: transparent;
border-style: solid;
border-width: 6px;
height: 0;
width: 0;
}
#tiptip_holder.tip_top #tiptip_arrow {
border-top-color: #fff;
border-top-color: rgba(255,255,255,0.35);
}
#tiptip_holder.tip_bottom #tiptip_arrow {
border-bottom-color: #fff;
border-bottom-color: rgba(255,255,255,0.35);
}
#tiptip_holder.tip_right #tiptip_arrow {
border-right-color: #fff;
border-right-color: rgba(255,255,255,0.35);
}
#tiptip_holder.tip_left #tiptip_arrow {
border-left-color: #fff;
border-left-color: rgba(255,255,255,0.35);
}
#tiptip_holder.tip_top #tiptip_arrow_inner {
margin-top: -7px;
margin-left: -6px;
border-top-color: rgb(25,25,25);
border-top-color: rgba(25,25,25,0.92);
}
#tiptip_holder.tip_bottom #tiptip_arrow_inner {
margin-top: -5px;
margin-left: -6px;
border-bottom-color: rgb(25,25,25);
border-bottom-color: rgba(25,25,25,0.92);
}
#tiptip_holder.tip_right #tiptip_arrow_inner {
margin-top: -6px;
margin-left: -5px;
border-right-color: rgb(25,25,25);
border-right-color: rgba(25,25,25,0.92);
}
#tiptip_holder.tip_left #tiptip_arrow_inner {
margin-top: -6px;
margin-left: -7px;
border-left-color: rgb(25,25,25);
border-left-color: rgba(25,25,25,0.92);
}
/* Webkit Hacks */
@media screen and (-webkit-min-device-pixel-ratio:0) {
#tiptip_content {
padding: 4px 8px 5px 8px;
background-color: rgba(45,45,45,0.88);
}
#tiptip_holder.tip_bottom #tiptip_arrow_inner {
border-bottom-color: rgba(45,45,45,0.88);
}
#tiptip_holder.tip_top #tiptip_arrow_inner {
border-top-color: rgba(20,20,20,0.92);
}
}

View File

@ -0,0 +1,29 @@
<?php
// These are the functions for building the dropdowns in the file browser
require_once ("includes/vars.php");
require_once ("includes/functions.php");
require_once ("international.php");
require_once ("player/".$prefs['player_backend']."/player.php");
require_once ("player/mpd/filetree.php");
require_once ("skins/".$skin."/ui_elements.php");
$error = 0;
$dbterms = array( 'tags' => null, 'rating' => null );
$path = (array_key_exists('path', $_REQUEST)) ? $_REQUEST['path'] : "";
$prefix = (array_key_exists('prefix', $_REQUEST)) ? $_REQUEST['prefix'].'_' : "dirholder";
$player = new fileCollector();
if ($player->is_connected()) {
if ($path == "") {
// print '<div class="configtitle textcentre expand" style="margin-left:8px"><b>'.get_int_text('button_file_browser').'</b></div>';
} else {
directoryControlHeader($prefix);
}
$player->doFileBrowse($path, $prefix);
} else {
header("HTTP/1.1 500 Internal Server Error");
}
?>

BIN
www/jukebox/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,5 @@
@import url("https://fonts.googleapis.com/css?family=Alegreya:400,400i,700,700i&amp;subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext,vietnamese");
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Alegreya", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "AppleGothic", "Gothic Uralic", sans-serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Arima+Madurai:400,700');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Arima Madurai", cursive;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Cabin:400,400i,700,700i&subset=latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Cabin", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, BetecknaLowerCase, Arial, sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Calisto MT", "Bookman Old Style", Bookman, "Goudy Old Style", Garamond, "Hoefler Text", "Bitstream Charter", Georgia, serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700&subset=cyrillic,cyrillic-ext,greek,latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Comfortaa", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Copperplate, "Copperplate Gothic Light", "Libris ADF Std", fantasy;
}

View File

@ -0,0 +1,6 @@
@import url('https://fonts.googleapis.com/css?family=Cormorant+Garamond:400,400i,700,700i&subset=cyrillic,latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Cormorant Garamond", serif;
line-height: 140%;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Crimson+Text:400,400i,700,700i');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Crimson Text", serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Darker+Grotesque:400,700&display=swap&subset=latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Darker Grotesque", sans-serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Dosis:400,700&subset=latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Dosis", sans-serif;
}

View File

@ -0,0 +1,5 @@
@import url("https://fonts.googleapis.com/css?family=Fahkwang:400,400i,700,700i&amp;subset=latin-ext,thai,vietnamese");
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Fahkwang", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family:CupolaUnicode,fantasy;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Futura, "Trebuchet MS", Arial, sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Garamond, Baskerville, "Baskerville Old Face", "Hoefler Text", "Times New Roman", serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Geneva, "URW Gothic L", Tahoma, Verdana, sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family:Georgia, serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Gill Sans", "Droid Sans", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family:breip,UnPilgia,cursive;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: Herculanum, "Licorice Strings BRK", fantasy;
}

View File

@ -0,0 +1,6 @@
@import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,400i,700,700i&subset=cyrillic,cyrillic-ext,latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "IBM Plex Sans", sans-serif;
line-height: 140%;
}

View File

@ -0,0 +1,6 @@
@import url('https://fonts.googleapis.com/css?family=IBM+Plex+Serif:400,400i,700,700i&subset=cyrillic,cyrillic-ext,latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "IBM Plex Serif", serif;
line-height: 140%;
}

View File

@ -0,0 +1,6 @@
@import url('https://fonts.googleapis.com/css?family=Josefin+Sans:400,400i,700,700i&subset=latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Josefin Sans", sans-serif;
line-height: 140%;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Josefin+Slab:400,400i,700,700i');body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Josefin Slab", serif;
line-height: 140%;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Libre+Franklin:400,400i,700,700i&subset=latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Libre Franklin", sans-serif;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Lora:400,400i,700,700i&subset=cyrillic,cyrillic-ext,latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Lora", serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family:"Lucida Grande", "Lucida Sans Unicode", "Linux Biolinum O", sans-serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Luminari", "Mathematica6", "eufm10", serif;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Marker Felt", "Backlash BRK", fantasy;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Mirza:400,700&subset=arabic,latin-ext');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: "Mirza", cursive;
}

View File

@ -0,0 +1,4 @@
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family:"Courier New", Courier, monospace;
}

View File

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,400i,700,700i&subset=cyrillic,cyrillic-ext,latin-ext,vietnamese');
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input,button,select,option
{
font-family: 'Montserrat', sans-serif;
}

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