mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-19 10:05:30 +01:00
comit with last rembo10 commit
This commit is contained in:
@@ -109,10 +109,11 @@ header { min-height: 68px; width: 100%; min-width: 935px; padding-left: 0px; pad
|
||||
h1 { font-size: 24px; }
|
||||
h2 { font-size: 20px; }
|
||||
h3 { font-size: 16px; }
|
||||
h4 { font-size: 14px; }
|
||||
|
||||
p.indented { padding-top: 20px; margin-left: 20px; font-size: 14px; }
|
||||
p.center { text-align: center; font-size: 18px; }
|
||||
.smalltext2 { font-size: 11px; margin-left: 45px; }
|
||||
.smalltext2 { font-size: 12px; margin-left: 45px; }
|
||||
|
||||
div#updatebar { text-align: center; min-width: 970px; width: 100%; background-color: light-blue; float: left; }
|
||||
div#logo { float: left; padding-left: 10px; }
|
||||
@@ -157,35 +158,45 @@ div#paddingheader { padding-top: 48px; font-size: 24px; font-weight: bold; text-
|
||||
div#nopaddingheader { font-size: 24px; font-weight: bold; text-align: center; }
|
||||
table#album_table { background-color: white; }
|
||||
|
||||
table#album_table th#select { vertical-align: middle; text-align: left; min-width: 25px; }
|
||||
table#album_table th#select { vertical-align: middle; text-align: left; min-width: 10px; }
|
||||
table#album_table th#albumart { text-align: left; min-width: 50px; }
|
||||
table#album_table th#albumname { text-align: center; min-width: 150px; }
|
||||
table#album_table th#reldate { width: 175px; text-align: center; min-width: 100px; }
|
||||
table#album_table th#status { width: 175px; text-align: center; min-width: 100px; }
|
||||
table#album_table th#reldate { width: 175px; text-align: center; min-width: 70px; }
|
||||
table#album_table th#status { width: 175px; text-align: center; min-width: 80px; }
|
||||
table#album_table th#type { width: 175px; text-align: center; min-width: 100px; }
|
||||
table#album_table th#bitrate { text-align: center; min-width: 60px; }
|
||||
table#album_table td#select { vertical-align: middle; text-align: left; }
|
||||
table#album_table td#albumart { vertical-align: middle; text-align: left; }
|
||||
table#album_table td#albumname { vertical-align: middle; text-align: center; }
|
||||
table#album_table td#reldate { vertical-align: middle; text-align: center; }
|
||||
table#album_table td#status { vertical-align: middle; text-align: center; }
|
||||
table#album_table td#status { vertical-align: middle; text-align: center; font-size: 13px; }
|
||||
table#album_table td#type { vertical-align: middle; text-align: center; }
|
||||
table#album_table td#have { vertical-align: middle; }
|
||||
table#album_table td#bitrate { vertical-align: middle; text-align: center; font-size: 13px; }
|
||||
|
||||
img.albumArt { float: left; padding-right: 5px; }
|
||||
div#albumheader { padding-top: 48px; height: 200px; }
|
||||
div#track_wrapper { padding-top: 20px; text-align: center; font-size: 16px; }
|
||||
div#track_wrapper { margin-left: -50px; padding-top: 20px; font-size: 16px; width: 100%; }
|
||||
|
||||
table#track_table th#number { text-align: right; min-width: 20px; }
|
||||
table#track_table th#number { text-align: right; min-width: 10px; }
|
||||
table#track_table th#name { text-align: center; min-width: 350px; }
|
||||
table#track_table th#duration { width: 175px; text-align: center; min-width: 100px; }
|
||||
table#track_table th#have { width: 175px; text-align: center; min-width: 100px; }
|
||||
table#track_table th#location { text-align: center; width: 250px; }
|
||||
table#track_table th#bitrate { text-align: center; min-width: 75px; }
|
||||
|
||||
table#track_table td#number { vertical-align: middle; text-align: right; }
|
||||
table#track_table td#name { vertical-align: middle; text-align: center; }
|
||||
table#track_table td#name { vertical-align: middle; text-align: center; font-size: 15px; }
|
||||
table#track_table td#duration { vertical-align: middle; text-align: center; }
|
||||
table#track_table td#have { vertical-align: middle; text-align: center; }
|
||||
table#track_table td#location { vertical-align: middle; text-align: center; font-size: 11px; }
|
||||
table#track_table td#bitrate { vertical-align: middle; text-align: center; font-size: 12px; }
|
||||
|
||||
table#history_table { background-color: white; width: 100%; }
|
||||
table#history_table { background-color: white; width: 100%; font-size: 13px; }
|
||||
|
||||
table#history_table td#dateadded { vertical-align: middle; text-align: center; min-width: 150px; font-size: 14px; }
|
||||
table#history_table td#filename { vertical-align: middle; text-align: center; min-width: 100px; font-size: 15px; }
|
||||
table#history_table td#size { vertical-align: middle; text-align: center; min-width: 75px; font-size: 14px; }
|
||||
table#history_table td#status { vertical-align: middle; text-align: center; font-size: 14px; }
|
||||
table#history_table td#action { vertical-align: middle; text-align: center; font-size: 14px; }
|
||||
|
||||
table#log_table { background-color: white; }
|
||||
|
||||
|
||||
@@ -51,35 +51,55 @@
|
||||
<th id="number">#</th>
|
||||
<th id="name">Track Title</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="have"></th>
|
||||
<th id="location">Local File</th>
|
||||
<th id="bitrate">Bit Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%
|
||||
i = 0
|
||||
%>
|
||||
%for track in tracks:
|
||||
<%
|
||||
i += 1
|
||||
have = myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ? AND TrackTitle like ?', [track['ArtistName'], track['AlbumTitle'], track['TrackTitle']])
|
||||
if len(have):
|
||||
if track['Location']:
|
||||
grade = 'A'
|
||||
check = '<img src="images/checkmark.png" alt="checkmark">'
|
||||
location = track['Location']
|
||||
else:
|
||||
grade = 'Z'
|
||||
check = ''
|
||||
grade = 'X'
|
||||
location = ''
|
||||
|
||||
if track['BitRate']:
|
||||
bitrate = str(track['BitRate']/1000) + ' kbps'
|
||||
else:
|
||||
bitrate = ''
|
||||
|
||||
try:
|
||||
trackduration = helpers.convert_milliseconds(track['TrackDuration'])
|
||||
except:
|
||||
trackduration = 'n/a'
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="number">${i}</td>
|
||||
<td id="number">${track['TrackNumber']}</td>
|
||||
<td id="name">${track['TrackTitle']}</td>
|
||||
<td id="duration">${trackduration}</td>
|
||||
<td id="have">${check}</td>
|
||||
<td id="location">${location}</td>
|
||||
<td id="bitrate">${bitrate}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
<%
|
||||
unmatched = myDB.select('SELECT * from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ?', [album['ArtistName'], album['AlbumTitle']])
|
||||
%>
|
||||
%if unmatched:
|
||||
%for track in unmatched:
|
||||
<%
|
||||
duration = helpers.convert_seconds(float(track['TrackLength']))
|
||||
%>
|
||||
<tr class="gradeC">
|
||||
<td id="number">${track['TrackNumber']}</td>
|
||||
<td id="name">${track['TrackTitle']}</td>
|
||||
<td id="duration">${duration}</td>
|
||||
<td id="location">${track['Location']}</td>
|
||||
<td id="bitrate">${int(track['BitRate'])/1000} kbps</td>
|
||||
</tr>
|
||||
%endfor
|
||||
%endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -97,6 +117,7 @@
|
||||
{
|
||||
$('#track_table').dataTable(
|
||||
{
|
||||
"aaSorting": [],
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false
|
||||
|
||||
@@ -41,11 +41,12 @@
|
||||
<tr>
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="albumart"></th>
|
||||
<th id="albumname">Album Name</th>
|
||||
<th id="reldate">Release Date</th>
|
||||
<th id="type">Release Type</th>
|
||||
<th id="albumname">Name</th>
|
||||
<th id="reldate">Date</th>
|
||||
<th id="type">Type</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="have">Have</th>
|
||||
<th id="bitrate">Bitrate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -62,7 +63,7 @@
|
||||
|
||||
myDB = db.DBConnection()
|
||||
totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=?', [album['AlbumID']]))
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ?', [album['ArtistName'], album['AlbumTitle']]))
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=? AND Location IS NOT NULL', [album['AlbumID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle LIKE ?', [album['ArtistName'], album['AlbumTitle']]))
|
||||
|
||||
try:
|
||||
percent = (havetracks*100.0)/totaltracks
|
||||
@@ -71,6 +72,12 @@
|
||||
except (ZeroDivisionError, TypeError):
|
||||
percent = 0
|
||||
totaltracks = '?'
|
||||
|
||||
avgbitrate = myDB.action("SELECT AVG(BitRate) FROM tracks WHERE AlbumID=?", [album['AlbumID']]).fetchone()[0]
|
||||
if avgbitrate:
|
||||
bitrate = str(int(avgbitrate)/1000) + ' kbps'
|
||||
else:
|
||||
bitrate = ''
|
||||
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
@@ -89,6 +96,7 @@
|
||||
%endif
|
||||
</td>
|
||||
<td id="have"><span title="${percent}"><span><div class="progress-container"><div style="width:${percent}%"><div class="havetracks">${havetracks}/${totaltracks}</div></div></div></td>
|
||||
<td id="bitrate">${bitrate}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
@@ -114,7 +122,8 @@
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{ "sType": "title-numeric"}
|
||||
{ "sType": "title-numeric"},
|
||||
null
|
||||
],
|
||||
"oLanguage": {
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<%
|
||||
import headphones
|
||||
from headphones import version
|
||||
%>
|
||||
<!doctype html>
|
||||
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
||||
@@ -70,6 +71,9 @@
|
||||
<footer>
|
||||
<div id="version">
|
||||
Version: ${headphones.CURRENT_VERSION}
|
||||
%if version.HEADPHONES_VERSION != 'master':
|
||||
(${version.HEADPHONES_VERSION})
|
||||
%endif
|
||||
</div>
|
||||
<div id="donate">
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<td>
|
||||
<h3>SABnzbd Host:</h3><input type="text" name="sab_host" value="${config['sab_host']}" size="30" maxlength="40"><br>
|
||||
|
||||
<i class="smalltext">usually localhost:8080</i>
|
||||
<i class="smalltext">usually http://localhost:8080</i>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Use Black Hole:</h3><input type="checkbox" name="blackhole" value=1 ${config['use_blackhole']} />
|
||||
<h3>Use Black Hole: <input type="checkbox" name="blackhole" value=1 ${config['use_blackhole']} /></h3>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Usenet Retention:</h3><input type="text" name="usenet_retention" value="${config['usenet_retention']}" size="20" maxlength="40">
|
||||
<h3>Usenet Retention:<input type="text" name="usenet_retention" value="${config['usenet_retention']}" size="20" maxlength="40"></h3>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -190,21 +190,21 @@
|
||||
<tr>
|
||||
<td>
|
||||
<h2>Album Quality:</h2><br>
|
||||
<input type="radio" name="preferred_quality" value="0" ${config['pref_qual_0']} /> Highest Quality excluding Lossless<br>
|
||||
<input type="radio" name="preferred_quality" value="1" ${config['pref_qual_1']} /> Highest Quality including Lossless<br>
|
||||
<input type="radio" name="preferred_quality" value="3" ${config['pref_qual_3']} /> Lossless Only<br>
|
||||
<input type="radio" name="preferred_quality" value="2" ${config['pref_qual_2']} /> Preferred Bitrate:
|
||||
<input type="text" name="preferred_bitrate" value="${config['pref_bitrate']}" size="5" maxlength="5" />kbps <br>
|
||||
<h4><input type="radio" name="preferred_quality" value="0" ${config['pref_qual_0']} /> Highest Quality excluding Lossless</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="1" ${config['pref_qual_1']} /> Highest Quality including Lossless</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="3" ${config['pref_qual_3']} /> Lossless Only</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="2" ${config['pref_qual_2']} /> Preferred Bitrate:
|
||||
<input type="text" name="preferred_bitrate" value="${config['pref_bitrate']}" size="3" maxlength="5" />kbps</h4>
|
||||
<i class="smalltext2"><input type="checkbox" name="detect_bitrate" value="1" ${config['detect_bitrate']} />Auto-Detect Preferred Bitrate </i>
|
||||
</td>
|
||||
<td>
|
||||
<h2>Post-Processing:</h2>
|
||||
<input type="checkbox" name="move_files" value="1" ${config['move_files']} /> Move downloads to Destination Folder<br />
|
||||
<input type="checkbox" name="rename_files" value="1" ${config['rename_files']} /> Rename files<br>
|
||||
<input type="checkbox" name="correct_metadata" value="1" ${config['correct_metadata']} /> Correct metadata<br>
|
||||
<input type="checkbox" name="cleanup_files" value="1" ${config['cleanup_files']} /> Delete leftover files (.m3u, .nfo, .sfv, .nzb, etc.)<br>
|
||||
<input type="checkbox" name="add_album_art" value="1" ${config['add_album_art']}> Add album art as 'folder.jpg' to album folder<br>
|
||||
<input type="checkbox" name="embed_album_art" value="1" ${config['embed_album_art']}> Embed album art in each file
|
||||
<h4><input type="checkbox" name="move_files" value="1" ${config['move_files']} /> Move downloads to Destination Folder</h4>
|
||||
<h4><input type="checkbox" name="rename_files" value="1" ${config['rename_files']} /> Rename files</h4>
|
||||
<h4><input type="checkbox" name="correct_metadata" value="1" ${config['correct_metadata']} /> Correct metadata</h4>
|
||||
<h4><input type="checkbox" name="cleanup_files" value="1" ${config['cleanup_files']} /> Delete leftover files (.m3u, .nfo, .sfv, .nzb, etc.)</h4>
|
||||
<h4><input type="checkbox" name="add_album_art" value="1" ${config['add_album_art']}> Add album art as 'folder.jpg' to album folder</h4>
|
||||
<h4><input type="checkbox" name="embed_album_art" value="1" ${config['embed_album_art']}> Embed album art in each file</h4>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
<td>
|
||||
<h2>Renaming Options:</h2>
|
||||
<br>
|
||||
<h3>Folder Format:</h3><input type="text" name="folder_format" value="${config['folder_format']}" size="43">
|
||||
<h3>Folder Format:</h3><input type="text" name="folder_format" value="${config['folder_format']}" size="43"><br>
|
||||
<i class="smalltext">Use: artist, album, year and first (first letter in artist name)<br />
|
||||
E.g.: first/artist/album [year] = G/Girl Talk/All Day [2010]</i>
|
||||
<br><br>
|
||||
@@ -256,6 +256,30 @@
|
||||
<h3>Log Directory:</h3><input type="text" name="log_dir" value="${config['log_dir']}" size="50">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h2>Re-Encoding Options:</h2>
|
||||
<br>
|
||||
<h3><input type="checkbox" name="encode" value="1" ${config['encode']}/>Convert Lossless to mp3</h3>
|
||||
<i class="smalltext">Note: this option requires the lame or ffmpeg encoder</i>
|
||||
<br><br>
|
||||
<%
|
||||
if config['encoder'] == 'lame':
|
||||
lameselect = 'selected="selected"'
|
||||
ffmpegselect = ''
|
||||
else:
|
||||
lameselect = ''
|
||||
ffmpegselect = 'selected="selected"'
|
||||
%>
|
||||
<h3>Encoder: <select name="encoder">
|
||||
<option value="lame" ${lameselect}>lame</option>
|
||||
<option value="ffmpeg" ${ffmpegselect}>ffmpeg</option>
|
||||
</select>
|
||||
Bitrate: <input type="text" name="bitrate" value="${config['bitrate']}" size="3" maxlength="5" />kbps <br>
|
||||
<br>
|
||||
<h3>Path to Encoder:</h3><input type="text" name="encoderfolder" value="${config['encoderfolder']}" size="43">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="dateadded">${item['DateAdded']}</td>
|
||||
<td id="filename"><a href="${item['URL']}">${item['Title']}</a></td>
|
||||
<td id="filename">${item['Title']} [<a href="${item['URL']}">nzb</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
|
||||
<td id="size">${helpers.bytes_to_mb(item['Size'])}</td>
|
||||
<td id="status">${item['Status']}</td>
|
||||
<td id="action">[<a href="queueAlbum?AlbumID=${item['AlbumID']}&redirect=history">retry</a>][<a href="queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history">new</a>]</td>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<%inherit file="base.html" />
|
||||
<%!
|
||||
import headphones
|
||||
from headphones.helpers import checked
|
||||
%>
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<ul id="subhead_menu">
|
||||
<li><a href="manageArtists">Manage Artists</a></li>
|
||||
<li><a href="manageArtists">Manage Artists</a></li>
|
||||
%if not headphones.ADD_ARTISTS:
|
||||
<li><a href="manageNew">Manage New Artists</a></li>
|
||||
%endif
|
||||
</ul>
|
||||
</div>
|
||||
</%def>
|
||||
@@ -30,6 +34,9 @@
|
||||
<input type="text" value="Enter a Music Directory to scan" onfocus="if
|
||||
(this.value==this.defaultValue) this.value='';" name="path" size="70" />
|
||||
%endif
|
||||
<br>
|
||||
<h3><input type="checkbox" name="autoadd" value="1" ${checked(headphones.ADD_ARTISTS)}>Automatically add new artists</h3>
|
||||
<br><br>
|
||||
<input type="submit" /></form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="name">Artist Name</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="album">Latest Album</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -32,11 +33,22 @@
|
||||
grade = 'C'
|
||||
else:
|
||||
grade = 'Z'
|
||||
|
||||
if artist['ReleaseDate'] and artist['LatestAlbum']:
|
||||
releasedate = artist['ReleaseDate']
|
||||
albumdisplay = '<i>%s</i> (%s)' % (artist['LatestAlbum'], artist['ReleaseDate'])
|
||||
elif artist['LatestAlbum']:
|
||||
releasedate = ''
|
||||
albumdisplay = '<i>%s</i>' % artist['LatestAlbum']
|
||||
else:
|
||||
releasedate = ''
|
||||
albumdisplay = '<i>None</i>'
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="select"><input type="checkbox" name="${artist['ArtistID']}" class="checkbox" /></td>
|
||||
<td id="name"><span title="${artist['ArtistSortName']}"></span><a href="artistPage?ArtistID=${artist['ArtistID']}">${artist['ArtistName']}</a></td>
|
||||
<td id="status">${artist['Status']}</td>
|
||||
<td id="album"><span title="${releasedate}"></span><a href="albumPage?AlbumID=${artist['AlbumID']}">${albumdisplay}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
@@ -58,7 +70,8 @@
|
||||
"aoColumns": [
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null
|
||||
null,
|
||||
{ "sType": "title-string"}
|
||||
],
|
||||
"bStateSave": true,
|
||||
"bPaginate": false
|
||||
|
||||
52
data/interfaces/default/managenew.html
Normal file
52
data/interfaces/default/managenew.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<%inherit file="base.html" />
|
||||
<%!
|
||||
import headphones
|
||||
%>
|
||||
<%def name="body()">
|
||||
<div id="paddingheader">
|
||||
<h1>Manage New Artists<h1>
|
||||
<h3><a href="musicScan?path=${headphones.MUSIC_DIR}&redirect=manageNew">Scan Music Library</a></h3>
|
||||
</div>
|
||||
<form action="addArtists" method="get">
|
||||
<p class="indented">
|
||||
Add selected artists
|
||||
<input type="submit" value="Go">
|
||||
</p>
|
||||
<table class="display" id="artist_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="name">Artist Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for artist in headphones.NEW_ARTISTS:
|
||||
<tr class="gradeZ">
|
||||
<td id="select"><input type="checkbox" name="${artist}" class="checkbox" /></td>
|
||||
<td id="name">${artist}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="css/data_table.css">
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function()
|
||||
{
|
||||
$('#artist_table').dataTable(
|
||||
{
|
||||
"aaSorting": [[1, 'asc']],
|
||||
"bStateSave": false,
|
||||
"bPaginate": false
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
@@ -51,35 +51,55 @@
|
||||
<th id="number">#</th>
|
||||
<th id="name">Track Title</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="have"></th>
|
||||
<th id="location">Local File</th>
|
||||
<th id="bitrate">Bit Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%
|
||||
i = 0
|
||||
%>
|
||||
%for track in tracks:
|
||||
<%
|
||||
i += 1
|
||||
have = myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ? AND TrackTitle like ?', [track['ArtistName'], track['AlbumTitle'], track['TrackTitle']])
|
||||
if len(have):
|
||||
if track['Location']:
|
||||
grade = 'A'
|
||||
check = '<img src="images/checkmark.png" alt="checkmark">'
|
||||
location = track['Location']
|
||||
else:
|
||||
grade = 'Z'
|
||||
check = ''
|
||||
grade = 'X'
|
||||
location = ''
|
||||
|
||||
if track['BitRate']:
|
||||
bitrate = str(track['BitRate']/1000) + ' kbps'
|
||||
else:
|
||||
bitrate = ''
|
||||
|
||||
try:
|
||||
trackduration = helpers.convert_milliseconds(track['TrackDuration'])
|
||||
except:
|
||||
trackduration = 'n/a'
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="number">${i}</td>
|
||||
<td id="number">${track['TrackNumber']}</td>
|
||||
<td id="name">${track['TrackTitle']}</td>
|
||||
<td id="duration">${trackduration}</td>
|
||||
<td id="have">${check}</td>
|
||||
<td id="location">${location}</td>
|
||||
<td id="bitrate">${bitrate}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
<%
|
||||
unmatched = myDB.select('SELECT * from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ?', [album['ArtistName'], album['AlbumTitle']])
|
||||
%>
|
||||
%if unmatched:
|
||||
%for track in unmatched:
|
||||
<%
|
||||
duration = helpers.convert_seconds(float(track['TrackLength']))
|
||||
%>
|
||||
<tr class="gradeC">
|
||||
<td id="number">${track['TrackNumber']}</td>
|
||||
<td id="name">${track['TrackTitle']}</td>
|
||||
<td id="duration">${duration}</td>
|
||||
<td id="location">${track['Location']}</td>
|
||||
<td id="bitrate">${int(track['BitRate'])/1000} kbps</td>
|
||||
</tr>
|
||||
%endfor
|
||||
%endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -97,6 +117,7 @@
|
||||
{
|
||||
$('#track_table').dataTable(
|
||||
{
|
||||
"aaSorting": [],
|
||||
"bFilter": false,
|
||||
"bInfo": false,
|
||||
"bPaginate": false
|
||||
|
||||
@@ -41,11 +41,12 @@
|
||||
<tr>
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="albumart"></th>
|
||||
<th id="albumname">Album Name</th>
|
||||
<th id="reldate">Release Date</th>
|
||||
<th id="type">Release Type</th>
|
||||
<th id="albumname">Name</th>
|
||||
<th id="reldate">Date</th>
|
||||
<th id="type">Type</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="have">Have</th>
|
||||
<th id="bitrate">Bitrate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -62,7 +63,7 @@
|
||||
|
||||
myDB = db.DBConnection()
|
||||
totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=?', [album['AlbumID']]))
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ?', [album['ArtistName'], album['AlbumTitle']]))
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=? AND Location IS NOT NULL', [album['AlbumID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle LIKE ?', [album['ArtistName'], album['AlbumTitle']]))
|
||||
|
||||
try:
|
||||
percent = (havetracks*100.0)/totaltracks
|
||||
@@ -71,6 +72,12 @@
|
||||
except (ZeroDivisionError, TypeError):
|
||||
percent = 0
|
||||
totaltracks = '?'
|
||||
|
||||
avgbitrate = myDB.action("SELECT AVG(BitRate) FROM tracks WHERE AlbumID=?", [album['AlbumID']]).fetchone()[0]
|
||||
if avgbitrate:
|
||||
bitrate = str(int(avgbitrate)/1000) + ' kbps'
|
||||
else:
|
||||
bitrate = ''
|
||||
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
@@ -89,6 +96,7 @@
|
||||
%endif
|
||||
</td>
|
||||
<td id="have"><span title="${percent}"><span><div class="progress-container"><div style="width:${percent}%"><div class="havetracks">${havetracks}/${totaltracks}</div></div></div></td>
|
||||
<td id="bitrate">${bitrate}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
@@ -114,7 +122,8 @@
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{ "sType": "title-numeric"}
|
||||
{ "sType": "title-numeric"},
|
||||
null
|
||||
],
|
||||
"oLanguage": {
|
||||
"sLengthMenu":"Show _MENU_ albums per page",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<%
|
||||
import headphones
|
||||
from headphones import version
|
||||
%>
|
||||
<!doctype html>
|
||||
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
||||
@@ -70,6 +71,9 @@
|
||||
<footer>
|
||||
<div id="version">
|
||||
Version: ${headphones.CURRENT_VERSION}
|
||||
%if version.HEADPHONES_VERSION != 'master':
|
||||
(${version.HEADPHONES_VERSION})
|
||||
%endif
|
||||
</div>
|
||||
<div id="donate">
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<td>
|
||||
<h3>SABnzbd Host:</h3><input type="text" name="sab_host" value="${config['sab_host']}" size="30" maxlength="40"><br>
|
||||
|
||||
<i class="smalltext">usually localhost:8080</i>
|
||||
<i class="smalltext">usually http://localhost:8080</i>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Use Black Hole:</h3><input type="checkbox" name="blackhole" value=1 ${config['use_blackhole']} />
|
||||
<h3>Use Black Hole: <input type="checkbox" name="blackhole" value=1 ${config['use_blackhole']} /></h3>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Usenet Retention:</h3><input type="text" name="usenet_retention" value="${config['usenet_retention']}" size="20" maxlength="40">
|
||||
<h3>Usenet Retention:<input type="text" name="usenet_retention" value="${config['usenet_retention']}" size="20" maxlength="40"></h3>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -190,21 +190,21 @@
|
||||
<tr>
|
||||
<td>
|
||||
<h2>Album Quality:</h2><br>
|
||||
<input type="radio" name="preferred_quality" value="0" ${config['pref_qual_0']} /> Highest Quality excluding Lossless<br>
|
||||
<input type="radio" name="preferred_quality" value="1" ${config['pref_qual_1']} /> Highest Quality including Lossless<br>
|
||||
<input type="radio" name="preferred_quality" value="3" ${config['pref_qual_3']} /> Lossless Only<br>
|
||||
<input type="radio" name="preferred_quality" value="2" ${config['pref_qual_2']} /> Preferred Bitrate:
|
||||
<input type="text" name="preferred_bitrate" value="${config['pref_bitrate']}" size="5" maxlength="5" />kbps <br>
|
||||
<h4><input type="radio" name="preferred_quality" value="0" ${config['pref_qual_0']} /> Highest Quality excluding Lossless</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="1" ${config['pref_qual_1']} /> Highest Quality including Lossless</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="3" ${config['pref_qual_3']} /> Lossless Only</h4>
|
||||
<h4><input type="radio" name="preferred_quality" value="2" ${config['pref_qual_2']} /> Preferred Bitrate:
|
||||
<input type="text" name="preferred_bitrate" value="${config['pref_bitrate']}" size="3" maxlength="5" />kbps</h4>
|
||||
<i class="smalltext2"><input type="checkbox" name="detect_bitrate" value="1" ${config['detect_bitrate']} />Auto-Detect Preferred Bitrate </i>
|
||||
</td>
|
||||
<td>
|
||||
<h2>Post-Processing:</h2>
|
||||
<input type="checkbox" name="move_files" value="1" ${config['move_files']} /> Move downloads to Destination Folder<br />
|
||||
<input type="checkbox" name="rename_files" value="1" ${config['rename_files']} /> Rename files<br>
|
||||
<input type="checkbox" name="correct_metadata" value="1" ${config['correct_metadata']} /> Correct metadata<br>
|
||||
<input type="checkbox" name="cleanup_files" value="1" ${config['cleanup_files']} /> Delete leftover files (.m3u, .nfo, .sfv, .nzb, etc.)<br>
|
||||
<input type="checkbox" name="add_album_art" value="1" ${config['add_album_art']}> Add album art as 'folder.jpg' to album folder<br>
|
||||
<input type="checkbox" name="embed_album_art" value="1" ${config['embed_album_art']}> Embed album art in each file
|
||||
<h4><input type="checkbox" name="move_files" value="1" ${config['move_files']} /> Move downloads to Destination Folder</h4>
|
||||
<h4><input type="checkbox" name="rename_files" value="1" ${config['rename_files']} /> Rename files</h4>
|
||||
<h4><input type="checkbox" name="correct_metadata" value="1" ${config['correct_metadata']} /> Correct metadata</h4>
|
||||
<h4><input type="checkbox" name="cleanup_files" value="1" ${config['cleanup_files']} /> Delete leftover files (.m3u, .nfo, .sfv, .nzb, etc.)</h4>
|
||||
<h4><input type="checkbox" name="add_album_art" value="1" ${config['add_album_art']}> Add album art as 'folder.jpg' to album folder</h4>
|
||||
<h4><input type="checkbox" name="embed_album_art" value="1" ${config['embed_album_art']}> Embed album art in each file</h4>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
<td>
|
||||
<h2>Renaming Options:</h2>
|
||||
<br>
|
||||
<h3>Folder Format:</h3><input type="text" name="folder_format" value="${config['folder_format']}" size="43">
|
||||
<h3>Folder Format:</h3><input type="text" name="folder_format" value="${config['folder_format']}" size="43"><br>
|
||||
<i class="smalltext">Use: artist, album, year and first (first letter in artist name)<br />
|
||||
E.g.: first/artist/album [year] = G/Girl Talk/All Day [2010]</i>
|
||||
<br><br>
|
||||
@@ -256,6 +256,30 @@
|
||||
<h3>Log Directory:</h3><input type="text" name="log_dir" value="${config['log_dir']}" size="50">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h2>Re-Encoding Options:</h2>
|
||||
<br>
|
||||
<h3><input type="checkbox" name="encode" value="1" ${config['encode']}/>Convert Lossless to mp3</h3>
|
||||
<i class="smalltext">Note: this option requires the lame or ffdshow encoder</i>
|
||||
<br><br>
|
||||
<%
|
||||
if config['encoder'] == 'lame':
|
||||
lameselect = 'selected="selected"'
|
||||
ffmpegselect = ''
|
||||
else:
|
||||
lameselect = ''
|
||||
ffmpegselect = 'selected="selected"'
|
||||
%>
|
||||
<h3>Encoder: <select name="encoder">
|
||||
<option value="lame" ${lameselect}>lame</option>
|
||||
<option value="ffmpeg" ${ffmpegselect}>ffmpeg</option>
|
||||
</select>
|
||||
Bitrate: <input type="text" name="bitrate" value="${config['bitrate']}" size="3" maxlength="5" />kbps <br>
|
||||
<br>
|
||||
<h3>Path to Encoder:</h3><input type="text" name="encoderfolder" value="${config['encoderfolder']}" size="43">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="dateadded">${item['DateAdded']}</td>
|
||||
<td id="filename"><a href="${item['URL']}">${item['Title']}</a></td>
|
||||
<td id="filename">${item['Title']} [<a href="${item['URL']}">nzb</a>]<a href="albumPage?AlbumID=${item['AlbumID']}">[album page]</a></td>
|
||||
<td id="size">${helpers.bytes_to_mb(item['Size'])}</td>
|
||||
<td id="status">${item['Status']}</td>
|
||||
<td id="action">[<a href="queueAlbum?AlbumID=${item['AlbumID']}&redirect=history">retry</a>][<a href="queueAlbum?AlbumID=${item['AlbumID']}&new=True&redirect=history">new</a>]</td>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<%inherit file="base.html" />
|
||||
<%!
|
||||
import headphones
|
||||
from headphones.helpers import checked
|
||||
%>
|
||||
<%def name="headerIncludes()">
|
||||
<div id="subhead_container">
|
||||
<ul id="subhead_menu">
|
||||
<li><a href="manageArtists">Manage Artists</a></li>
|
||||
<li><a href="manageArtists">Manage Artists</a></li>
|
||||
%if not headphones.ADD_ARTISTS:
|
||||
<li><a href="manageNew">Manage New Artists</a></li>
|
||||
%endif
|
||||
</ul>
|
||||
</div>
|
||||
</%def>
|
||||
@@ -30,6 +34,9 @@
|
||||
<input type="text" value="Enter a Music Directory to scan" onfocus="if
|
||||
(this.value==this.defaultValue) this.value='';" name="path" size="70" />
|
||||
%endif
|
||||
<br>
|
||||
<h3><input type="checkbox" name="autoadd" value="1" ${checked(headphones.ADD_ARTISTS)}>Automatically add new artists</h3>
|
||||
<br><br>
|
||||
<input type="submit" /></form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="name">Artist Name</th>
|
||||
<th id="status">Status</th>
|
||||
<th id="album">Latest Album</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -32,11 +33,22 @@
|
||||
grade = 'C'
|
||||
else:
|
||||
grade = 'Z'
|
||||
|
||||
if artist['ReleaseDate'] and artist['LatestAlbum']:
|
||||
releasedate = artist['ReleaseDate']
|
||||
albumdisplay = '<i>%s</i> (%s)' % (artist['LatestAlbum'], artist['ReleaseDate'])
|
||||
elif artist['LatestAlbum']:
|
||||
releasedate = ''
|
||||
albumdisplay = '<i>%s</i>' % artist['LatestAlbum']
|
||||
else:
|
||||
releasedate = ''
|
||||
albumdisplay = '<i>None</i>'
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
<td id="select"><input type="checkbox" name="${artist['ArtistID']}" class="checkbox" /></td>
|
||||
<td id="name"><span title="${artist['ArtistSortName']}"></span><a href="artistPage?ArtistID=${artist['ArtistID']}">${artist['ArtistName']}</a></td>
|
||||
<td id="status">${artist['Status']}</td>
|
||||
<td id="album"><span title="${releasedate}"></span><a href="albumPage?AlbumID=${artist['AlbumID']}">${albumdisplay}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
@@ -58,7 +70,8 @@
|
||||
"aoColumns": [
|
||||
null,
|
||||
{ "sType": "title-string"},
|
||||
null
|
||||
null,
|
||||
{ "sType": "title-string"}
|
||||
],
|
||||
"bStateSave": true,
|
||||
"bPaginate": false
|
||||
|
||||
52
data/interfaces/remix/managenew.html
Normal file
52
data/interfaces/remix/managenew.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<%inherit file="base.html" />
|
||||
<%!
|
||||
import headphones
|
||||
%>
|
||||
<%def name="body()">
|
||||
<div id="paddingheader">
|
||||
<h1>Manage New Artists<h1>
|
||||
<h3><a href="musicScan?path=${headphones.MUSIC_DIR}&redirect=manageNew">Scan Music Library</a></h3>
|
||||
</div>
|
||||
<form action="addArtists" method="get">
|
||||
<p class="indented">
|
||||
Add selected artists
|
||||
<input type="submit" value="Go">
|
||||
</p>
|
||||
<table class="display" id="artist_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="select"><input type="checkbox" onClick="toggle(this)" /></th>
|
||||
<th id="name">Artist Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for artist in headphones.NEW_ARTISTS:
|
||||
<tr class="gradeZ">
|
||||
<td id="select"><input type="checkbox" name="${artist}" class="checkbox" /></td>
|
||||
<td id="name">${artist}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="css/data_table.css">
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function()
|
||||
{
|
||||
$('#artist_table').dataTable(
|
||||
{
|
||||
"aaSorting": [[1, 'asc']],
|
||||
"bStateSave": false,
|
||||
"bPaginate": false
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
@@ -11,7 +11,7 @@ from lib.configobj import ConfigObj
|
||||
|
||||
import cherrypy
|
||||
|
||||
from headphones import updater, searcher, importer, versioncheck, logger, postprocessor, version, sab
|
||||
from headphones import updater, searcher, importer, versioncheck, logger, postprocessor, version, sab, librarysync
|
||||
from headphones.common import *
|
||||
|
||||
FULL_PATH = None
|
||||
@@ -62,6 +62,8 @@ PATH_TO_XML = None
|
||||
PREFERRED_QUALITY = None
|
||||
PREFERRED_BITRATE = None
|
||||
DETECT_BITRATE = False
|
||||
ADD_ARTISTS = False
|
||||
NEW_ARTISTS = []
|
||||
CORRECT_METADATA = False
|
||||
MOVE_FILES = False
|
||||
RENAME_FILES = False
|
||||
@@ -75,7 +77,7 @@ USENET_RETENTION = None
|
||||
INCLUDE_EXTRAS = False
|
||||
|
||||
NZB_SEARCH_INTERVAL = 360
|
||||
LIBRARYSCAN_INTERVAL = 60
|
||||
LIBRARYSCAN_INTERVAL = 300
|
||||
DOWNLOAD_SCAN_INTERVAL = 5
|
||||
|
||||
SAB_HOST = None
|
||||
@@ -107,7 +109,7 @@ MEDIA_FORMATS = ["mp3", "flac", "aac", "ogg", "ape", "m4a"]
|
||||
INTERFACE = None
|
||||
FOLDER_PERMISSIONS = None
|
||||
|
||||
ENCODE = None
|
||||
ENCODE = False
|
||||
ENCODERFOLDER = None
|
||||
ENCODER = None
|
||||
BITRATE = None
|
||||
@@ -166,7 +168,7 @@ def initialize():
|
||||
global __INITIALIZED__, FULL_PATH, PROG_DIR, QUIET, DAEMON, DATA_DIR, CONFIG_FILE, CFG, LOG_DIR, CACHE_DIR, \
|
||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, LAUNCH_BROWSER, GIT_PATH, \
|
||||
CURRENT_VERSION, LATEST_VERSION, MUSIC_DIR, DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \
|
||||
CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, \
|
||||
ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, \
|
||||
ADD_ALBUM_ART, EMBED_ALBUM_ART, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, NZB_SEARCH_INTERVAL, \
|
||||
LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \
|
||||
@@ -206,6 +208,7 @@ def initialize():
|
||||
PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0)
|
||||
PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '')
|
||||
DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0))
|
||||
ADD_ARTISTS = bool(check_setting_int(CFG, 'General', 'auto_add_artists', 1))
|
||||
CORRECT_METADATA = bool(check_setting_int(CFG, 'General', 'correct_metadata', 0))
|
||||
MOVE_FILES = bool(check_setting_int(CFG, 'General', 'move_files', 0))
|
||||
RENAME_FILES = bool(check_setting_int(CFG, 'General', 'rename_files', 0))
|
||||
@@ -221,7 +224,7 @@ def initialize():
|
||||
INCLUDE_EXTRAS = bool(check_setting_int(CFG, 'General', 'include_extras', 0))
|
||||
|
||||
NZB_SEARCH_INTERVAL = check_setting_int(CFG, 'General', 'nzb_search_interval', 360)
|
||||
LIBRARYSCAN_INTERVAL = check_setting_int(CFG, 'General', 'libraryscan_interval', 180)
|
||||
LIBRARYSCAN_INTERVAL = check_setting_int(CFG, 'General', 'libraryscan_interval', 300)
|
||||
DOWNLOAD_SCAN_INTERVAL = check_setting_int(CFG, 'General', 'download_scan_interval', 5)
|
||||
|
||||
SAB_HOST = check_setting_str(CFG, 'SABnzbd', 'sab_host', '')
|
||||
@@ -253,11 +256,9 @@ def initialize():
|
||||
|
||||
ENCODERFOLDER = check_setting_str(CFG, 'General', 'encoderfolder', '')
|
||||
ENCODER = check_setting_str(CFG, 'General', 'encoder', 'ffmpeg')
|
||||
BITRATE = check_setting_str(CFG, 'General', 'bitrate', '128')
|
||||
SAMPLINGFREQUENCY= check_setting_str(CFG, 'General', 'samplingfrequency', '44100')
|
||||
ENCODE = check_setting_str(CFG, 'General', 'encode', 'false')
|
||||
|
||||
|
||||
BITRATE = check_setting_int(CFG, 'General', 'bitrate', 192)
|
||||
SAMPLINGFREQUENCY= check_setting_int(CFG, 'General', 'samplingfrequency', 44100)
|
||||
ENCODE = bool(check_setting_int(CFG, 'General', 'encode', 0))
|
||||
|
||||
if not LOG_DIR:
|
||||
LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
@@ -378,6 +379,7 @@ def config_write():
|
||||
new_config['General']['preferred_quality'] = PREFERRED_QUALITY
|
||||
new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE
|
||||
new_config['General']['detect_bitrate'] = int(DETECT_BITRATE)
|
||||
new_config['General']['auto_add_artists'] = int(ADD_ARTISTS)
|
||||
new_config['General']['correct_metadata'] = int(CORRECT_METADATA)
|
||||
new_config['General']['move_files'] = int(MOVE_FILES)
|
||||
new_config['General']['rename_files'] = int(RENAME_FILES)
|
||||
@@ -427,7 +429,7 @@ def config_write():
|
||||
new_config['General']['interface'] = INTERFACE
|
||||
new_config['General']['folder_permissions'] = FOLDER_PERMISSIONS
|
||||
|
||||
new_config['General']['encode'] = ENCODE
|
||||
new_config['General']['encode'] = int(ENCODE)
|
||||
new_config['General']['encoder'] = ENCODER
|
||||
new_config['General']['bitrate'] = BITRATE
|
||||
new_config['General']['samplingfrequency'] = SAMPLINGFREQUENCY
|
||||
@@ -446,7 +448,7 @@ def start():
|
||||
|
||||
SCHED.add_cron_job(updater.dbUpdate, hour=4, minute=0, second=0)
|
||||
SCHED.add_interval_job(searcher.searchNZB, minutes=NZB_SEARCH_INTERVAL)
|
||||
SCHED.add_interval_job(importer.scanMusic, minutes=LIBRARYSCAN_INTERVAL)
|
||||
SCHED.add_interval_job(librarysync.libraryScan, minutes=LIBRARYSCAN_INTERVAL)
|
||||
SCHED.add_interval_job(versioncheck.checkGithub, minutes=300)
|
||||
SCHED.add_interval_job(postprocessor.checkFolder, minutes=DOWNLOAD_SCAN_INTERVAL)
|
||||
|
||||
@@ -460,9 +462,9 @@ def dbcheck():
|
||||
c=conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT, IncludeExtras INTEGER, LatestAlbum TEXT, ReleaseDate TEXT, AlbumID TEXT, HaveTracks INTEGER, TotalTracks INTEGER)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT, Type TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER, Location TEXT, BitRate INTEGER, CleanName TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS snatched (AlbumID TEXT, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT, FolderName TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS have (ArtistName TEXT, AlbumTitle TEXT, TrackNumber TEXT, TrackTitle TEXT, TrackLength TEXT, BitRate TEXT, Genre TEXT, Date TEXT, TrackID TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS have (ArtistName TEXT, AlbumTitle TEXT, TrackNumber TEXT, TrackTitle TEXT, TrackLength TEXT, BitRate TEXT, Genre TEXT, Date TEXT, TrackID TEXT, Location TEXT, CleanName TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS lastfmcloud (ArtistName TEXT, ArtistID TEXT, Count INTEGER)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS descriptions (ReleaseGroupID TEXT, ReleaseID TEXT, Summary TEXT, Content TEXT)')
|
||||
c.execute('CREATE TABLE IF NOT EXISTS releases (ReleaseID TEXT, ReleaseGroupID TEXT, UNIQUE(ReleaseID, ReleaseGroupID))')
|
||||
@@ -510,7 +512,32 @@ def dbcheck():
|
||||
try:
|
||||
c.execute('SELECT FolderName from snatched')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE snatched ADD COLUMN FolderName TEXT')
|
||||
c.execute('ALTER TABLE snatched ADD COLUMN FolderName TEXT')
|
||||
|
||||
try:
|
||||
c.execute('SELECT Location from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN Location TEXT')
|
||||
|
||||
try:
|
||||
c.execute('SELECT Location from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN Location TEXT')
|
||||
|
||||
try:
|
||||
c.execute('SELECT BitRate from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN BitRate INTEGER')
|
||||
|
||||
try:
|
||||
c.execute('SELECT CleanName from tracks')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE tracks ADD COLUMN CleanName TEXT')
|
||||
|
||||
try:
|
||||
c.execute('SELECT CleanName from have')
|
||||
except sqlite3.OperationalError:
|
||||
c.execute('ALTER TABLE have ADD COLUMN CleanName TEXT')
|
||||
|
||||
conn.commit()
|
||||
c.close()
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import os
|
||||
import headphones
|
||||
import argparse
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from subprocess import call
|
||||
|
||||
try:
|
||||
import argparse
|
||||
except ImportError:
|
||||
import lib.argparse as argparse
|
||||
|
||||
def encode(albumPath):
|
||||
|
||||
tempDirEncode=os.path.join(albumPath,"temp")
|
||||
@@ -21,7 +25,7 @@ def encode(albumPath):
|
||||
|
||||
for r,d,f in os.walk(albumPath):
|
||||
for music in f:
|
||||
if any(music.endswith('.' + x) for x in headphones.MEDIA_FORMATS):
|
||||
if any(music.endswith('.' + x) for x in ["flac", "m4a", "wav"]):
|
||||
musicFiles.append(os.path.join(r, music))
|
||||
musicTempFiles.append(os.path.join(tempDirEncode, music))
|
||||
|
||||
|
||||
@@ -84,6 +84,16 @@ def convert_milliseconds(ms):
|
||||
|
||||
return minutes
|
||||
|
||||
def convert_seconds(s):
|
||||
|
||||
gmtime = time.gmtime(s)
|
||||
if s > 3600:
|
||||
minutes = time.strftime("%H:%M:%S", gmtime)
|
||||
else:
|
||||
minutes = time.strftime("%M:%S", gmtime)
|
||||
|
||||
return minutes
|
||||
|
||||
def today():
|
||||
today = datetime.date.today()
|
||||
yyyymmdd = datetime.date.isoformat(today)
|
||||
@@ -104,6 +114,13 @@ def replace_all(text, dic):
|
||||
text = text.replace(i, j)
|
||||
return text
|
||||
|
||||
def cleanName(string):
|
||||
|
||||
pass1 = latinToAscii(string).lower()
|
||||
out_string = re.sub('[\.\-\/\!\@\#\$\%\^\&\*\(\)\+\-\"\'\,\;\:\[\]\{\}\<\>\=\_]', '', pass1).encode('utf-8')
|
||||
|
||||
return out_string
|
||||
|
||||
def extract_data(s):
|
||||
|
||||
from headphones import logger
|
||||
@@ -143,4 +160,35 @@ def extract_logline(s):
|
||||
message = match.group("message")
|
||||
return (timestamp, level, thread, message)
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
def extract_song_data(s):
|
||||
|
||||
#headphones default format
|
||||
music_dir = headphones.MUSIC_DIR
|
||||
folder_format = headphones.FOLDER_FORMAT
|
||||
file_format = headphones.FILE_FORMAT
|
||||
|
||||
full_format = os.path.join(headphones.MUSIC_DIR)
|
||||
pattern = re.compile(r'(?P<name>.*?)\s\-\s(?P<album>.*?)\s\[(?P<year>.*?)\]', re.VERBOSE)
|
||||
match = pattern.match(s)
|
||||
|
||||
if match:
|
||||
name = match.group("name")
|
||||
album = match.group("album")
|
||||
year = match.group("year")
|
||||
return (name, album, year)
|
||||
else:
|
||||
logger.info("Couldn't parse " + s + " into a valid default format")
|
||||
|
||||
#newzbin default format
|
||||
pattern = re.compile(r'(?P<name>.*?)\s\-\s(?P<album>.*?)\s\((?P<year>\d+?\))', re.VERBOSE)
|
||||
match = pattern.match(s)
|
||||
if match:
|
||||
name = match.group("name")
|
||||
album = match.group("album")
|
||||
year = match.group("year")
|
||||
return (name, album, year)
|
||||
else:
|
||||
logger.info("Couldn't parse " + s + " into a valid Newbin format")
|
||||
return (name, album, year)
|
||||
@@ -7,89 +7,6 @@ import headphones
|
||||
from headphones import logger, helpers, db, mb, albumart, lastfm
|
||||
|
||||
various_artists_mbid = '89ad4ac3-39f7-470e-963a-56509c546377'
|
||||
|
||||
def scanMusic(dir=None):
|
||||
|
||||
if not dir:
|
||||
dir = headphones.MUSIC_DIR
|
||||
|
||||
try:
|
||||
dir = str(dir)
|
||||
except UnicodeEncodeError:
|
||||
dir = unicode(dir).encode('unicode_escape')
|
||||
|
||||
logger.info('Starting Music Scan with directory: %s' % dir)
|
||||
|
||||
results = []
|
||||
|
||||
for r,d,f in os.walk(dir):
|
||||
for files in f:
|
||||
if any(files.endswith('.' + x) for x in headphones.MEDIA_FORMATS):
|
||||
results.append(os.path.join(r, files))
|
||||
|
||||
logger.info(u'%i music files found. Reading metadata....' % len(results))
|
||||
|
||||
if results:
|
||||
|
||||
myDB = db.DBConnection()
|
||||
myDB.action('''DELETE from have''')
|
||||
|
||||
for song in results:
|
||||
try:
|
||||
f = MediaFile(song)
|
||||
except:
|
||||
logger.warn('Could not read file: %s' % song)
|
||||
continue
|
||||
else:
|
||||
if f.albumartist:
|
||||
artist = f.albumartist
|
||||
elif f.artist:
|
||||
artist = f.artist
|
||||
else:
|
||||
continue
|
||||
|
||||
if not f.album:
|
||||
album = None
|
||||
else:
|
||||
album = f.album
|
||||
|
||||
myDB.action('INSERT INTO have VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?)', [artist, album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid])
|
||||
|
||||
# Get the average bitrate if the option is selected
|
||||
if headphones.DETECT_BITRATE:
|
||||
try:
|
||||
avgbitrate = myDB.action("SELECT AVG(BitRate) FROM have").fetchone()[0]
|
||||
headphones.PREFERRED_BITRATE = int(avgbitrate)/1000
|
||||
|
||||
except Exception, e:
|
||||
logger.error('Error detecting preferred bitrate:' + str(e))
|
||||
|
||||
artistlist = myDB.action('SELECT DISTINCT ArtistName FROM have').fetchall()
|
||||
logger.info(u"Preparing to import %i artists" % len(artistlist))
|
||||
|
||||
artistlist_to_mbids(artistlist)
|
||||
|
||||
def itunesImport(pathtoxml):
|
||||
|
||||
if os.path.splitext(pathtoxml)[1] == '.xml':
|
||||
logger.info(u"Loading xml file from"+ pathtoxml)
|
||||
pl = XMLLibraryParser(pathtoxml)
|
||||
l = Library(pl.dictionary)
|
||||
lst = []
|
||||
for song in l.songs:
|
||||
lst.append(song.artist)
|
||||
rawlist = {}.fromkeys(lst).keys()
|
||||
artistlist = [f for f in rawlist if f != None]
|
||||
|
||||
else:
|
||||
rawlist = os.listdir(pathtoxml)
|
||||
logger.info(u"Loading artists from directory:" +pathtoxml)
|
||||
exclude = ['.ds_store', 'various artists', 'untitled folder', 'va']
|
||||
artistlist = [f for f in rawlist if f.lower() not in exclude]
|
||||
|
||||
logger.info('Starting directory/xml import...')
|
||||
artistlist_to_mbids(artistlist)
|
||||
|
||||
|
||||
def is_exists(artistid):
|
||||
|
||||
@@ -99,19 +16,23 @@ def is_exists(artistid):
|
||||
artistlist = myDB.select('SELECT ArtistID, ArtistName from artists WHERE ArtistID=?', [artistid])
|
||||
|
||||
if any(artistid in x for x in artistlist):
|
||||
logger.debug(artistlist[0][1] + u" is already in the database. Updating 'have tracks', but not artist information")
|
||||
logger.info(artistlist[0][1] + u" is already in the database. Updating 'have tracks', but not artist information")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def artistlist_to_mbids(artistlist):
|
||||
def artistlist_to_mbids(artistlist, forced=False):
|
||||
|
||||
for artist in artistlist:
|
||||
|
||||
results = mb.findArtist(artist['ArtistName'], limit=1)
|
||||
|
||||
if forced:
|
||||
artist = unicode(artist, 'utf-8')
|
||||
|
||||
results = mb.findArtist(artist, limit=1)
|
||||
|
||||
if not results:
|
||||
logger.info('No results found for: %' % artist)
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -124,16 +45,12 @@ def artistlist_to_mbids(artistlist):
|
||||
# Add to database if it doesn't exist
|
||||
if artistid != various_artists_mbid and not is_exists(artistid):
|
||||
addArtisttoDB(artistid)
|
||||
|
||||
# Update track count regardless of whether it already exists
|
||||
if artistid != various_artists_mbid:
|
||||
|
||||
|
||||
# Just update the tracks if it does
|
||||
else:
|
||||
myDB = db.DBConnection()
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']]))
|
||||
|
||||
controlValueDict = {"ArtistID": artistid}
|
||||
newValueDict = {"HaveTracks": havetracks}
|
||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist]))
|
||||
myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artistid])
|
||||
|
||||
# Update the similar artist tag cloud:
|
||||
logger.info('Updating artist information from Last.fm')
|
||||
@@ -228,9 +145,10 @@ def addArtisttoDB(artistid, extrasonly=False):
|
||||
myDB.action('DELETE from albums WHERE AlbumID=?', [release['releaseid']])
|
||||
myDB.action('DELETE from tracks WHERE AlbumID=?', [release['releaseid']])
|
||||
|
||||
myDB.action('DELETE from tracks WHERE AlbumID=?', [rg['id']])
|
||||
for track in release_dict['tracks']:
|
||||
|
||||
cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title'])
|
||||
|
||||
controlValueDict = {"TrackID": track['id'],
|
||||
"AlbumID": rg['id']}
|
||||
newValueDict = {"ArtistID": artistid,
|
||||
@@ -239,14 +157,27 @@ def addArtisttoDB(artistid, extrasonly=False):
|
||||
"AlbumASIN": release_dict['asin'],
|
||||
"TrackTitle": track['title'],
|
||||
"TrackDuration": track['duration'],
|
||||
"TrackNumber": track['number']
|
||||
"TrackNumber": track['number'],
|
||||
"CleanName": cleanname
|
||||
}
|
||||
|
||||
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE TrackID=?', [track['id']]).fetchone()
|
||||
|
||||
if not match:
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE CleanName=?', [cleanname]).fetchone()
|
||||
if not match:
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [artist['artist_name'], rg['title'], track['title']]).fetchone()
|
||||
if match:
|
||||
newValueDict['Location'] = match['Location']
|
||||
newValueDict['BitRate'] = match['BitRate']
|
||||
myDB.action('DELETE from have WHERE Location=?', [match['Location']])
|
||||
|
||||
myDB.upsert("tracks", newValueDict, controlValueDict)
|
||||
|
||||
latestalbum = myDB.action('SELECT AlbumTitle, ReleaseDate, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [artistid]).fetchone()
|
||||
totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid]))
|
||||
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['artist_name']]))
|
||||
|
||||
controlValueDict = {"ArtistID": artistid}
|
||||
|
||||
if latestalbum:
|
||||
@@ -254,9 +185,12 @@ def addArtisttoDB(artistid, extrasonly=False):
|
||||
"LatestAlbum": latestalbum['AlbumTitle'],
|
||||
"ReleaseDate": latestalbum['ReleaseDate'],
|
||||
"AlbumID": latestalbum['AlbumID'],
|
||||
"TotalTracks": totaltracks}
|
||||
"TotalTracks": totaltracks,
|
||||
"HaveTracks": havetracks}
|
||||
else:
|
||||
newValueDict = {"Status": "Active"}
|
||||
newValueDict = {"Status": "Active",
|
||||
"TotalTracks": totaltracks,
|
||||
"HaveTracks": havetracks}
|
||||
|
||||
myDB.upsert("artists", newValueDict, controlValueDict)
|
||||
logger.info(u"Updating complete for: " + artist['artist_name'])
|
||||
@@ -339,6 +273,8 @@ def addReleaseById(rid):
|
||||
|
||||
for track in release_dict['tracks']:
|
||||
|
||||
cleanname = helpers.cleanName(release_dict['artist_name'] + ' ' + release_dict['rg_title'] + ' ' + track['title'])
|
||||
|
||||
controlValueDict = {"TrackID": track['id'],
|
||||
"AlbumID": rgid}
|
||||
newValueDict = {"ArtistID": release_dict['artist_id'],
|
||||
@@ -347,8 +283,22 @@ def addReleaseById(rid):
|
||||
"AlbumASIN": release_dict['asin'],
|
||||
"TrackTitle": track['title'],
|
||||
"TrackDuration": track['duration'],
|
||||
"TrackNumber": track['number']
|
||||
"TrackNumber": track['number'],
|
||||
"CleanName": cleanname
|
||||
}
|
||||
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE TrackID=?', [track['id']]).fetchone()
|
||||
|
||||
if not match:
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE CleanName=?', [cleanname]).fetchone()
|
||||
|
||||
if not match:
|
||||
match = myDB.action('SELECT Location, BitRate from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [release_dict['artist_name'], release_dict['rg_title'], track['title']]).fetchone()
|
||||
|
||||
if match:
|
||||
newValueDict['Location'] = match['Location']
|
||||
newValueDict['BitRate'] = match['BitRate']
|
||||
myDB.action('DELETE from have WHERE Location=?', [match['Location']])
|
||||
|
||||
myDB.upsert("tracks", newValueDict, controlValueDict)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import urllib
|
||||
from xml.dom import minidom
|
||||
from collections import defaultdict
|
||||
import random
|
||||
import time
|
||||
|
||||
import headphones
|
||||
from headphones import db, logger
|
||||
@@ -19,7 +20,16 @@ def getSimilar():
|
||||
for result in results[:12]:
|
||||
|
||||
url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key)
|
||||
data = urllib.urlopen(url).read()
|
||||
|
||||
try:
|
||||
data = urllib.urlopen(url).read()
|
||||
except:
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
if len(data) < 200:
|
||||
continue
|
||||
|
||||
d = minidom.parseString(data)
|
||||
node = d.documentElement
|
||||
artists = d.getElementsByTagName("artist")
|
||||
|
||||
178
headphones/librarysync.py
Normal file
178
headphones/librarysync.py
Normal file
@@ -0,0 +1,178 @@
|
||||
import os
|
||||
import glob
|
||||
|
||||
from lib.beets.mediafile import MediaFile
|
||||
|
||||
import headphones
|
||||
from headphones import db, logger, helpers, importer
|
||||
|
||||
def libraryScan(dir=None):
|
||||
|
||||
if not dir:
|
||||
dir = headphones.MUSIC_DIR
|
||||
|
||||
logger.info('Scanning music directory: %s' % dir)
|
||||
|
||||
new_artists = []
|
||||
bitrates = []
|
||||
|
||||
myDB = db.DBConnection()
|
||||
myDB.action('DELETE from have')
|
||||
|
||||
for r,d,f in os.walk(dir):
|
||||
for files in f:
|
||||
# MEDIA_FORMATS = music file extensions, e.g. mp3, flac, etc
|
||||
if any(files.endswith('.' + x) for x in headphones.MEDIA_FORMATS):
|
||||
file = os.path.join(r, files)
|
||||
# Try to read the metadata
|
||||
try:
|
||||
f = MediaFile(file)
|
||||
except:
|
||||
logger.error('Cannot read file: ' + file)
|
||||
continue
|
||||
|
||||
# Grab the bitrates for the auto detect bit rate option
|
||||
if f.bitrate:
|
||||
bitrates.append(f.bitrate)
|
||||
|
||||
# Try to match on metadata first, starting with the track id
|
||||
if f.mb_trackid:
|
||||
|
||||
# Wondering if theres a better way to do this -> do one thing if the row exists,
|
||||
# do something else if it doesn't
|
||||
track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [f.mb_trackid]).fetchone()
|
||||
|
||||
if track:
|
||||
myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [file, f.bitrate, track['TrackID']])
|
||||
continue
|
||||
|
||||
# Try to find a match based on artist/album/tracktitle
|
||||
if f.albumartist:
|
||||
f_artist = f.albumartist
|
||||
elif f.artist:
|
||||
f_artist = f.artist
|
||||
else:
|
||||
continue
|
||||
|
||||
if f_artist and f.album and f.title:
|
||||
|
||||
track = myDB.action('SELECT TrackID from tracks WHERE CleanName LIKE ?', [helpers.cleanName(f_artist +' '+f.album+' '+f.title)]).fetchone()
|
||||
|
||||
if not track:
|
||||
track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f_artist, f.album, f.title]).fetchone()
|
||||
|
||||
if track:
|
||||
myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [file, f.bitrate, track['TrackID']])
|
||||
continue
|
||||
|
||||
# if we can't find a match in the database on a track level, it might be a new artist or it might be on a non-mb release
|
||||
new_artists.append(f_artist)
|
||||
|
||||
# The have table will become the new database for unmatched tracks (i.e. tracks with no associated links in the database
|
||||
myDB.action('INSERT INTO have VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [f_artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid, file, helpers.cleanName(f_artist+' '+f.album+' '+f.title)])
|
||||
|
||||
# Now check empty file paths to see if we can find a match based on their folder format
|
||||
tracks = myDB.select('SELECT * from tracks WHERE Location IS NULL')
|
||||
for track in tracks:
|
||||
|
||||
release = myDB.action('SELECT * from albums WHERE AlbumID=?', [track['AlbumID']]).fetchone()
|
||||
|
||||
try:
|
||||
year = release['ReleaseDate'][:4]
|
||||
except TypeError:
|
||||
year = ''
|
||||
|
||||
artist = release['ArtistName'].replace('/', '_')
|
||||
album = release['AlbumTitle'].replace('/', '_')
|
||||
|
||||
if release['ArtistName'].startswith('The '):
|
||||
sortname = release['ArtistName'][4:]
|
||||
else:
|
||||
sortname = release['ArtistName']
|
||||
|
||||
if sortname.isdigit():
|
||||
firstchar = '0-9'
|
||||
else:
|
||||
firstchar = sortname[0]
|
||||
|
||||
|
||||
albumvalues = { 'artist': artist,
|
||||
'album': album,
|
||||
'year': year,
|
||||
'first': firstchar,
|
||||
}
|
||||
|
||||
|
||||
folder = helpers.replace_all(headphones.FOLDER_FORMAT, albumvalues)
|
||||
folder = folder.replace('./', '_/').replace(':','_').replace('?','_')
|
||||
|
||||
if folder.endswith('.'):
|
||||
folder = folder.replace(folder[len(folder)-1], '_')
|
||||
|
||||
if not track['TrackNumber']:
|
||||
tracknumber = ''
|
||||
else:
|
||||
tracknumber = '%02d' % track['TrackNumber']
|
||||
|
||||
trackvalues = { 'tracknumber': tracknumber,
|
||||
'title': track['TrackTitle'],
|
||||
'artist': release['ArtistName'],
|
||||
'album': release['AlbumTitle'],
|
||||
'year': year
|
||||
}
|
||||
|
||||
new_file_name = helpers.replace_all(headphones.FILE_FORMAT, trackvalues).replace('/','_') + '.*'
|
||||
|
||||
new_file_name = new_file_name.replace('?','_').replace(':', '_')
|
||||
|
||||
full_path_to_file = os.path.normpath(os.path.join(headphones.MUSIC_DIR, folder, new_file_name))
|
||||
|
||||
match = glob.glob(full_path_to_file)
|
||||
|
||||
if match:
|
||||
|
||||
myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [match[0], track['TrackID']])
|
||||
myDB.action('DELETE from have WHERE Location=?', [match[0]])
|
||||
|
||||
# Try to insert the appropriate track id so we don't have to keep doing this
|
||||
try:
|
||||
f = MediaFile(match[0])
|
||||
f.mb_trackid = track['TrackID']
|
||||
f.save()
|
||||
myDB.action('UPDATE tracks SET BitRate=? WHERE TrackID=?', [f.bitrate, track['TrackID']])
|
||||
logger.debug('Wrote mbid to track: %s' % match[0])
|
||||
except:
|
||||
logger.error('Error embedding track id into: %s' % match[0])
|
||||
continue
|
||||
|
||||
# Clean up bad filepaths
|
||||
tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL')
|
||||
|
||||
for track in tracks:
|
||||
if not os.path.isfile(track['Location']):
|
||||
myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [None, track['TrackID']])
|
||||
|
||||
logger.info('Completed scanning of directory: %s. Updating track counts' % dir)
|
||||
|
||||
# Clean up the new artist list
|
||||
unique_artists = {}.fromkeys(new_artists).keys()
|
||||
current_artists = myDB.select('SELECT ArtistName, ArtistID from artists')
|
||||
|
||||
artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]]
|
||||
|
||||
# Update track counts
|
||||
for artist in current_artists:
|
||||
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID like ? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']]))
|
||||
myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']])
|
||||
|
||||
logger.info('Found %i new artists' % len(artist_list))
|
||||
|
||||
if headphones.ADD_ARTISTS:
|
||||
logger.info('Importing %i new artists' % len(artist_list))
|
||||
importer.artistlist_to_mbids(artist_list)
|
||||
else:
|
||||
logger.info('To add these artists, go to Manage->Manage New Artists')
|
||||
headphones.NEW_ARTISTS = artist_list
|
||||
|
||||
if headphones.DETECT_BITRATE:
|
||||
headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000
|
||||
@@ -452,7 +452,7 @@ def updateHave(albumpath):
|
||||
else:
|
||||
continue
|
||||
|
||||
myDB.action('INSERT INTO have VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?)', [artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid])
|
||||
myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song, f.bitrate, artist, f.album, f.title])
|
||||
|
||||
def renameUnprocessedFolder(albumpath):
|
||||
|
||||
|
||||
@@ -65,7 +65,10 @@ def sendNZB(nzb):
|
||||
params['mode'] = 'addfile'
|
||||
multiPartParams = {"nzbfile": (nzb.name+".nzb", nzb.extraInfo[0])}
|
||||
|
||||
url = "http://" + headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params)
|
||||
if not headphones.SAB_HOST.startswith('http://') or headphones.SAB_HOST.startswith('https://'):
|
||||
headphones.SAB_HOST = 'http://' + headphones.SAB_HOST
|
||||
|
||||
url = headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params)
|
||||
|
||||
try:
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ def checkGithub():
|
||||
|
||||
gh = github.GitHub()
|
||||
|
||||
for curCommit in gh.commits.forBranch('rembo10', 'headphones', 'master'):
|
||||
for curCommit in gh.commits.forBranch('rembo10', 'headphones', version.HEADPHONES_VERSION):
|
||||
if not latest_commit:
|
||||
latest_commit = curCommit.id
|
||||
if not cur_commit:
|
||||
@@ -200,5 +200,5 @@ def update():
|
||||
ver_file.write(headphones.LATEST_VERSION)
|
||||
ver_file.close()
|
||||
except IOError, e:
|
||||
logger.error(u"Unable to write version file, update not complete: "+ex(e))
|
||||
logger.error(u"Unable to write for curCommit in gh.commits.forBranch file, update not complete: "+ex(e))
|
||||
return
|
||||
@@ -10,7 +10,7 @@ import threading
|
||||
|
||||
import headphones
|
||||
|
||||
from headphones import logger, searcher, db, importer, mb, lastfm
|
||||
from headphones import logger, searcher, db, importer, mb, lastfm, librarysync
|
||||
from headphones.helpers import checked, radio
|
||||
|
||||
|
||||
@@ -148,7 +148,12 @@ class WebInterface(object):
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect("upcoming")
|
||||
markAlbums.exposed = True
|
||||
|
||||
|
||||
def addArtists(self, **args):
|
||||
threading.Thread(target=importer.artistlist_to_mbids, args=[args, True]).start()
|
||||
time.sleep(5)
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
addArtists.exposed = True
|
||||
|
||||
def queueAlbum(self, AlbumID, ArtistID=None, new=False, redirect=None):
|
||||
logger.info(u"Marking album: " + AlbumID + "as wanted...")
|
||||
@@ -189,6 +194,10 @@ class WebInterface(object):
|
||||
return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists)
|
||||
manageArtists.exposed = True
|
||||
|
||||
def manageNew(self):
|
||||
return serve_template(templatename="managenew.html", title="Manage New Artists")
|
||||
manageNew.exposed = True
|
||||
|
||||
def markArtists(self, action=None, **args):
|
||||
myDB = db.DBConnection()
|
||||
for ArtistID in args:
|
||||
@@ -227,15 +236,19 @@ class WebInterface(object):
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
importItunes.exposed = True
|
||||
|
||||
def musicScan(self, path):
|
||||
def musicScan(self, path, redirect=None, autoadd=0):
|
||||
headphones.ADD_ARTISTS = autoadd
|
||||
headphones.MUSIC_DIR = path
|
||||
headphones.config_write()
|
||||
try:
|
||||
threading.Thread(target=importer.scanMusic, args=[path]).start()
|
||||
threading.Thread(target=librarysync.libraryScan).start()
|
||||
except Exception, e:
|
||||
logger.error('Unable to complete the scan: %s' % e)
|
||||
time.sleep(10)
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
if redirect:
|
||||
raise cherrypy.HTTPRedirect(redirect)
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
musicScan.exposed = True
|
||||
|
||||
def forceUpdate(self):
|
||||
@@ -291,7 +304,7 @@ class WebInterface(object):
|
||||
|
||||
interface_dir = os.path.join(headphones.PROG_DIR, 'data/interfaces/')
|
||||
interface_list = [ name for name in os.listdir(interface_dir) if os.path.isdir(os.path.join(interface_dir, name)) ]
|
||||
|
||||
|
||||
config = {
|
||||
"http_host" : headphones.HTTP_HOST,
|
||||
"http_user" : headphones.HTTP_USERNAME,
|
||||
@@ -336,7 +349,11 @@ class WebInterface(object):
|
||||
"file_format" : headphones.FILE_FORMAT,
|
||||
"include_extras" : checked(headphones.INCLUDE_EXTRAS),
|
||||
"log_dir" : headphones.LOG_DIR,
|
||||
"interface_list" : interface_list
|
||||
"interface_list" : interface_list,
|
||||
"encode": checked(headphones.ENCODE),
|
||||
"encoder": headphones.ENCODER,
|
||||
"bitrate": headphones.BITRATE,
|
||||
"encoderfolder": headphones.ENCODERFOLDER
|
||||
}
|
||||
return serve_template(templatename="config.html", title="Settings", config=config)
|
||||
config.exposed = True
|
||||
@@ -346,8 +363,9 @@ class WebInterface(object):
|
||||
sab_host=None, sab_username=None, sab_apikey=None, sab_password=None, sab_category=None, download_dir=None, blackhole=0, blackhole_dir=None,
|
||||
usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None,
|
||||
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
|
||||
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, interface=None, log_dir=None):
|
||||
|
||||
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, interface=None, log_dir=None,
|
||||
encode=0, encoder=None, bitrate=None, encoderfolder=None):
|
||||
|
||||
headphones.HTTP_HOST = http_host
|
||||
headphones.HTTP_PORT = http_port
|
||||
headphones.HTTP_USERNAME = http_username
|
||||
@@ -389,6 +407,10 @@ class WebInterface(object):
|
||||
headphones.INCLUDE_EXTRAS = include_extras
|
||||
headphones.INTERFACE = interface
|
||||
headphones.LOG_DIR = log_dir
|
||||
headphones.ENCODE = encode
|
||||
headphones.ENCODER = encoder
|
||||
headphones.BITRATE = bitrate
|
||||
headphones.ENCODERFOLDER = encoderfolder
|
||||
|
||||
headphones.config_write()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user