Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Time to merge #339

Merged
merged 21 commits into from
Aug 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,24 @@ Some Handy Links
[Sonerezh on Twitter](https://twitter.com/snrzh).

Don't forget to [support us](https://www.sonerezh.bzh/support) !

development-new branch
----------------------

This is a branch to continue development while the main project is temporarily paused
Changes implemented:

- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/300 (Removed slow subquery from albums view)
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/304 (Changed select grouping so that albums with the same name are listed)
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/287 (Removed trailing slash in subdirectory path added by CakePHP for some folders)
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/306 (Upgraded CakePHP to 2.9.8)
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/293 (Implemented database cleaning)
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/309 (Corrected Songs number in statistics)
- Made compatible with MySQL setting ONLY_FULL_GROUP_BY + removed old unecessary SQLite code
- Implemented re-parsing of metadata for modified files
- Fixed some magic numbers in code
- Merged commit in PR https://github.com/Sonerezh/sonerezh/pull/312 (Player now shows artist instead of band)
- Bugfix for https://github.com/gs11/sonerezh/issues/7 (Re-parsing of metadata for modified files)
- Fixed detection of tag 'DISCNUMBER' without 'DISCTOTAL' for OGG files
- Fixed detection of disc number without a disc total in the string (e.g. '01' instead of '01/02')
- Fixed year not showing for album with multiple CDs
3 changes: 2 additions & 1 deletion app/Config/bootstrap.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,5 @@
define('THUMBNAILS_DIR', 'thumbnails');
define('RESIZED_DIR', IMAGES.'resized'.DS);
define('AVATARS_DIR', 'avatars');
define('DOCKER', false);
define('DOCKER', false);
define('SYNC_BATCH_SIZE', 100);
3 changes: 3 additions & 0 deletions app/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ private function __setLanguage() {
case 'fr':
$locale = 'fra';
break;
case 'de':
$locale = 'deu';
break;
default:
$locale = 'eng';
break;
Expand Down
6 changes: 3 additions & 3 deletions app/Controller/Component/SortComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class SortComponent extends Component {
*/
public function sortByBand($songs) {
foreach ($songs as $key => $row) {
$s_discs = preg_split('/\//', $row['Song']['disc']);
$s_discs = explode('/', $row['Song']['disc']);
$s_band[$key] = $row['Song']['band'];
$s_album[$key] = $row['Song']['album'];
$s_track_number[$key] = $row['Song']['track_number'];
Expand All @@ -43,7 +43,7 @@ public function sortByBand($songs) {
*/
public function sortByDisc($songs) {
foreach ($songs as $key => $row) {
$s_discs = preg_split('/\//', $row['Song']['disc']);
$s_discs = explode('/', $row['Song']['disc']);
$s_track_number[$key] = $row['Song']['track_number'];
$s_disc[$key] = $s_discs[0];
}
Expand All @@ -54,4 +54,4 @@ public function sortByDisc($songs) {

return $songs;
}
}
}
194 changes: 99 additions & 95 deletions app/Controller/SongsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,45 @@ public function import() {
$found_in_this_directory = $subdirectory->find('^.*\.(mp3|ogg|flac|aac)$');

// The find method does not return absolute paths.
foreach ($found_in_this_directory as $key => $value) {
foreach ($found_in_this_directory as $file) {

// CakePHP adds a trailing slash when the path contains two dots in sequence
if(substr($subdirectory->path, -1) === '/') {
$found_in_this_directory[$key] = $subdirectory->path . $value;
$found[$subdirectory->path . $file] = filemtime($subdirectory->path . $file);
} else {
$found_in_this_directory[$key] = $subdirectory->path . '/' . $value;
$found[$subdirectory->path . '/' . $file] = filemtime($subdirectory->path . '/' . $file);
}
}

$found = array_merge($found, $found_in_this_directory);
}
}
}

// The files already imported
$already_imported = $this->Song->find('list', array(
'fields' => array('Song.id', 'Song.source_path')
'fields' => array('Song.source_path', 'Song.modified')
));

// The difference between $found and $already_imported
$to_import = array_merge(array_diff($found, $already_imported));
$to_remove = array_merge(array_diff($already_imported, $found));
$to_import = array_keys(array_diff_key($found, $already_imported));
$to_remove = array_keys(array_diff_key($already_imported, $found));

// Find what already imported files still on the filesystem that have been modified since import
$to_update = array();
foreach (array_intersect_key($found, $already_imported) as $source_path => $value) {
if($value > strtotime($already_imported[$source_path])) {
$to_update[] = $source_path;
}
}

$to_import_count = count($to_import);
$to_update_count = count($to_update);
$to_remove_count = count($to_remove);
$found_count = count($found);
$diff_count = $found_count - $to_import_count;
$already_imported_count = count($already_imported);

$this->Session->write('to_import', $to_import);
$this->Session->write('to_update', $to_update);
$this->Session->write('to_remove', $to_remove);
$this->set(compact('to_import_count', 'to_remove_count', 'diff_count'));
$this->set(compact('to_import_count', 'to_remove_count', 'to_update_count', 'already_imported_count'));
} elseif ($this->request->is('post')) {
$this->viewClass = 'Json';
$update_result = array();
Expand All @@ -98,13 +107,15 @@ public function import() {
Cache::write('import', true);

$to_import = $this->Session->read('to_import');
$to_update = $this->Session->read('to_update');
$to_remove = $this->Session->read('to_remove');
$imported = array();
$updated = array();
$removed = array();

$i = 0;
foreach ($to_import as $file) {
if ($i >= 100) {
if ($i >= SYNC_BATCH_SIZE) {
break;
}

Expand All @@ -122,15 +133,43 @@ public function import() {
$update_result[$i]['message'] = $parse_result['message'];
}

$imported [] = $file;
$imported[] = $file;
$i++;
}

foreach ($to_update as $file) {
if ($i >= SYNC_BATCH_SIZE) {
break;
}

$song_manager = new SongManager($file);
$parse_result = $song_manager->parseMetadata();

// Get the song id and enrich the array
$result = $this->Song->find('first', array(
'fields' => array('Song.id'),
'conditions' => array('Song.source_path' => $file)
));
$parse_result['data']['id'] = $result['Song']['id'];

if (!$this->Song->save($parse_result['data'])) {
$update_result[$file]['status'] = 'ERR';
$update_result[$file]['message'] = __('Unable to update the song metadata in the database');
} else {
unset($parse_result['data']);
$update_result[$i]['file'] = $file;
$update_result[$i]['status'] = $parse_result['status'];
$update_result[$i]['message'] = $parse_result['message'];
}

$updated[] = $file;
$i++;
}

$this->loadModel('PlaylistMembership');
$this->Song->virtualFields['files_with_cover'] = 'count(Song2.id)';

foreach ($to_remove as $file) {
if ($i >= 100) {
if ($i >= SYNC_BATCH_SIZE) {
break;
}

Expand All @@ -143,38 +182,38 @@ public function import() {
'conditions' => array('Song2.cover = Song.cover')
)
),
'fields' => array('Song.id', 'Song.cover', 'files_with_cover'),
'fields' => array('MAX(Song.id) id', 'MAX(Song.cover) cover', 'COUNT(Song2.id) files_with_cover'),
'conditions' => array('Song.source_path' => $file)
));

// Remove song from database
$this->PlaylistMembership->deleteAll(array('PlaylistMembership.song_id' => $result["Song"]["id"]), false);
$this->PlaylistMembership->deleteAll(array('PlaylistMembership.song_id' => $result['0']['id']), false);

$update_result[$i]['file'] = $file;
if($this->Song->delete($result["Song"]["id"], false)) {
if($this->Song->delete($result['0']['id'], false)) {
$update_result[$i]['status'] = "OK";
$update_result[$i]['message'] = "";
} else {
$update_result[$i]['status'] = "ERR";
$update_result[$i]['message'] = "Unable to delete song from the database"; //TODO: Should be handled with __( function
$update_result[$i]['message'] = __('Unable to delete song from the database');
}

// Last file using this cover file
if ($result["Song"]['files_with_cover'] == 1) {
if ($result['0']['files_with_cover'] == 1) {
// Remove cover files from file system
if (file_exists(IMAGES.THUMBNAILS_DIR.DS . $result["Song"]["cover"])) {
unlink(IMAGES.THUMBNAILS_DIR.DS . $result["Song"]["cover"]);
if (file_exists(IMAGES.THUMBNAILS_DIR.DS . $result['0']['cover'])) {
unlink(IMAGES.THUMBNAILS_DIR.DS . $result['0']['cover']);
}

// Remove resized cover files from file system
$resized_filename_base = explode(".", $result["Song"]["cover"])[0];
$resized_filename_base = explode(".", $result['0']['cover'])[0];
$resized_files = glob(RESIZED_DIR . $resized_filename_base . "_*");
foreach ($resized_files as $resized_file) {
unlink($resized_file);
}
}

$removed [] = $file;
$removed[] = $file;
$i++;
}

Expand All @@ -188,10 +227,12 @@ public function import() {

$sync_token = $settings['Setting']['sync_token'];

$import_diff = array_diff($to_import, $imported);
$remove_diff = array_diff($to_remove, $removed);
$this->Session->write('to_import', $import_diff);
$this->Session->write('to_remove', $remove_diff);
$remaining_to_import = array_diff($to_import, $imported);
$remaining_to_update = array_diff($to_update, $updated);
$remaining_to_remove = array_diff($to_remove, $removed);
$this->Session->write('to_import', $remaining_to_import);
$this->Session->write('to_update', $remaining_to_update);
$this->Session->write('to_remove', $remaining_to_remove);
$this->set(compact('sync_token', 'update_result'));
$this->set('_serialize', array('sync_token', 'update_result'));
}
Expand Down Expand Up @@ -288,58 +329,26 @@ public function albums() {
$page = isset($this->request->params['named']['page']) ? $this->request->params['named']['page'] : 1;
$db = $this->Song->getDataSource();

// Ugly temporary fix for SQlite DB
if ($db->config['datasource'] == 'Database/Sqlite') {
$this->Song->virtualFields['cover'] = 'MIN(Song.cover)';

if ($page == 1) {
$latests = $this->Song->find('all', array(
'fields' => array('Song.id', 'Song.band', 'Song.album', 'Song.cover'),
'group' => 'Song.album',
'order' => 'Song.created DESC',
'limit' => 6
));
}

$this->Paginator->settings = array(
'Song' => array(
'fields' => array('Song.id', 'Song.band', 'Song.album', 'Song.cover'),
'group' => 'Song.album',
'order' => $sort,
'limit' => 36
)
);
} else {
$subQuery = $db->buildStatement(
array(
'fields' => array('MIN(subsong.id)', 'subsong.album'),
'table' => $db->fullTableName($this->Song),
'alias' => 'subsong',
'group' => 'subsong.album'
),
$this->Song
);
$subQuery = ' (Song.id, Song.album) IN (' . $subQuery . ') ';

if ($page == 1) {
$latests = $this->Song->find('all', array(
'fields' => array('Song.id', 'Song.band', 'Song.album', 'Song.cover'),
'conditions' => $subQuery,
'order' => 'Song.created DESC',
'limit' => 6
));
}

// This doesn't work on SQlite database
$this->Paginator->settings = array(
'Song' => array(
'fields' => array('Song.id', 'Song.band', 'Song.album', 'Song.cover'),
'conditions' => $subQuery,
'order' => $sort,
'limit' => 36
)
);
if ($page == 1) {
$latests = $this->Song->find('all', array(
'fields' => array('Song.band', 'Song.album', 'cover'),
'group' => array('Song.album', 'Song.band'),
'order' => 'MAX(Song.created) DESC',
'limit' => 6
));
}

$this->Paginator->settings = array(
'Song' => array(
'fields' => array('Song.band', 'Song.album', 'cover'),
'group' => array('Song.album', 'Song.band'),
'order' => $sort,
'limit' => 36
)
);

$songs = $this->Paginator->paginate();

foreach ($songs as &$song) {
Expand Down Expand Up @@ -375,13 +384,12 @@ public function album() {

$parsed = array();
foreach ($songs as &$song) {
$setsQuantity = explode('/', $song['Song']['disc']);

if (count($setsQuantity) < 2 || $setsQuantity[1] == '1') {
$currentDisc = '1';
} else {
$currentDisc = $setsQuantity[0];
$currentDisc = 1;
if (!empty($song['Song']['disc'])) {
$setsQuantity = explode('/', $song['Song']['disc']);
$currentDisc = (int)($setsQuantity[0]);
}

$parsed[$currentDisc][] = $song;
}

Expand Down Expand Up @@ -430,12 +438,10 @@ public function artists() {
// Then we can group the songs by band name, album and disc.
$parsed = array();
foreach ($songs as $song) {
$setsQuantity = preg_split('/\//', $song['Song']['disc']);

if (count($setsQuantity) < 2 || $setsQuantity[1] == '1') {
$currentDisc = '1';
} else {
$currentDisc = $setsQuantity[0];
$currentDisc = 1;
if (!empty($song['Song']['disc'])) {
$setsQuantity = explode('/', $song['Song']['disc']);
$currentDisc = (int)($setsQuantity[0]);
}

if (!isset($parsed[$song['Song']['band']]['albums'][$song['Song']['album']])) {
Expand Down Expand Up @@ -558,12 +564,10 @@ public function search() {

$parsed = array();
foreach ($songs as $song) {
$setsQuantity = preg_split('/\//', $song['Song']['disc']);

if (count($setsQuantity) < 2 || $setsQuantity[1] == '1' ) {
$currentDisc = '1';
} else {
$currentDisc = $setsQuantity[0];
$currentDisc = 1;
if (!empty($song['Song']['disc'])) {
$setsQuantity = explode('/', $song['Song']['disc']);
$currentDisc = (int)($setsQuantity[0]);
}

if (!isset($parsed[$song['Song']['band']]['albums'][$song['Song']['album']])) {
Expand Down
Loading