This commit is contained in:
58
services/cleanup_utiles.js
Normal file
58
services/cleanup_utiles.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var fs = require("fs");
|
||||
var notifier = require("./notifier");
|
||||
|
||||
var server = require("../server");
|
||||
var config = server.config;
|
||||
|
||||
var album_cover_dir = config.cache_folder + "/album_covers/";
|
||||
var album_cover_32 = album_cover_dir + "32/";
|
||||
var album_cover_96 = album_cover_dir + "96/";
|
||||
var album_cover_64 = album_cover_dir + "64/";
|
||||
var album_cover_128 = album_cover_dir + "128/";
|
||||
var album_cover_256 = album_cover_dir + "256/";
|
||||
|
||||
var artist_cover_dir = config.cache_folder + "/artist_covers/";
|
||||
var artist_cover_128 = artist_cover_dir + "128/";
|
||||
var artist_cover_256 = artist_cover_dir + "256/";
|
||||
var artist_cover_512 = artist_cover_dir + "512/";
|
||||
|
||||
var box_cover_dir = config.cache_folder + "/box_covers/";
|
||||
var box_cover_32 = box_cover_dir + "32/";
|
||||
var box_cover_64 = box_cover_dir + "64/";
|
||||
var box_cover_128 = box_cover_dir + "128/";
|
||||
var box_cover_256 = box_cover_dir + "256/";
|
||||
|
||||
var video_cover_dir = config.cache_folder + "/video_covers/";
|
||||
var video_cache_dir = config.cache_folder + "/realtime/";
|
||||
|
||||
var audio_cache_dir = config.cache_folder + "/audio_cache/";
|
||||
var audio_cache_128 = audio_cache_dir + "128/";
|
||||
|
||||
notifier.on("track_deleted", id => {
|
||||
fs.unlink(audio_cache_128 + id + ".mp3", () => {});
|
||||
});
|
||||
|
||||
notifier.on("artist_deleted", id => {
|
||||
fs.unlink(artist_cover_128 + id, () => {});
|
||||
fs.unlink(artist_cover_256 + id, () => {});
|
||||
fs.unlink(artist_cover_512 + id, () => {});
|
||||
});
|
||||
|
||||
notifier.on("album_deleted", id => {
|
||||
fs.unlink(album_cover_32 + id, () => {});
|
||||
fs.unlink(album_cover_64 + id, () => {});
|
||||
fs.unlink(album_cover_96 + id, () => {});
|
||||
fs.unlink(album_cover_128 + id, () => {});
|
||||
fs.unlink(album_cover_256 + id, () => {});
|
||||
});
|
||||
|
||||
notifier.on("box_deleted", id => {
|
||||
fs.unlink(box_cover_32 + id, () => {});
|
||||
fs.unlink(box_cover_64 + id, () => {});
|
||||
fs.unlink(box_cover_128 + id, () => {});
|
||||
fs.unlink(box_cover_256 + id, () => {});
|
||||
});
|
||||
|
||||
notifier.on("video_deleted", id => {
|
||||
fs.unlink(video_cover_dir + "/" + id + ".png", () => {});
|
||||
});
|
||||
96
services/cover/resizer.js
Normal file
96
services/cover/resizer.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const sharp = require("sharp");
|
||||
const fs = require("fs");
|
||||
|
||||
|
||||
/*
|
||||
RADIO COVER
|
||||
*/
|
||||
const radio_cover_sizes = [
|
||||
{ width: 128, height: 128, fit: "cover" },
|
||||
{ width: 64, height: 64, fit: "cover" },
|
||||
{ width: 32, height: 32, fit: "cover" }
|
||||
];
|
||||
exports.resize_image_for_radio = function (image, callback) {
|
||||
resizer(image, radio_cover_sizes, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
ALBUM COVER
|
||||
*/
|
||||
const album_cover_sizes = [
|
||||
{ width: 256, height: 256, fit: "cover" },
|
||||
{ width: 128, height: 128, fit: "cover" },
|
||||
{ width: 64, height: 64, fit: "cover" },
|
||||
{ width: 32, height: 32, fit: "cover" }
|
||||
];
|
||||
exports.resize_image_for_album = function (image, callback) {
|
||||
if (typeof image === 'string') {
|
||||
let data = fs.readFileSync(image);
|
||||
resizer(data, album_cover_sizes, callback);
|
||||
} else {
|
||||
resizer(image, album_cover_sizes, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
ARTIST COVER
|
||||
*/
|
||||
const artist_cover_sizes = [
|
||||
{ width: 1024, height: 512, fit: "cover" },
|
||||
{ width: 512, height: 256, fit: "cover" },
|
||||
{ width: 256, height: 128, fit: "cover" },
|
||||
{ width: 128, height: 64, fit: "cover" },
|
||||
{ width: 64, height: 32, fit: "cover" }
|
||||
];
|
||||
exports.resize_image_for_artist = function (image, callback) {
|
||||
if (typeof image === 'string') {
|
||||
console.log("resizing: " + image);
|
||||
let data = fs.readFileSync(image);
|
||||
resizer(data, artist_cover_sizes, callback);
|
||||
} else {
|
||||
resizer(image, artist_cover_sizes, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
BOX COVER
|
||||
*/
|
||||
const box_cover_sizes = [
|
||||
{ width: 256, height: 362, fit: "cover" },
|
||||
{ width: 128, height: 181, fit: "cover" },
|
||||
{ width: 64, height: 90, fit: "cover" },
|
||||
{ width: 32, height: 45, fit: "cover" }
|
||||
];
|
||||
exports.resize_image_for_box = function (image, callback) {
|
||||
if (typeof image === 'string') {
|
||||
let data = fs.readFileSync(image);
|
||||
resizer(data, box_cover_sizes, callback);
|
||||
} else {
|
||||
resizer(image, box_cover_sizes, callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function resizer(image, array, callback) {
|
||||
let result = {}
|
||||
let runner = (image, index) => {
|
||||
if (index < array.length) {
|
||||
let size = array[index];
|
||||
sharp(image)
|
||||
.resize(size)
|
||||
.png()
|
||||
.toBuffer()
|
||||
.then(data => {
|
||||
result["cover" + size.width] = "data:image/png;base64, " + Buffer.from(data).toString("base64");
|
||||
runner(data, ++index);
|
||||
}).catch(err=>{
|
||||
console.log(err);
|
||||
});
|
||||
} else {
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
runner(image, 0);
|
||||
}
|
||||
269
services/cover_excluder.js
Normal file
269
services/cover_excluder.js
Normal file
@@ -0,0 +1,269 @@
|
||||
console.log("service/cover_excluder INIT");
|
||||
console.log("service/cover_excluder REQUIRE fs");
|
||||
const fs = require("fs");
|
||||
console.log("service/cover_excluder REQUIRE path");
|
||||
const path = require("path");
|
||||
console.log("service/cover_excluder REQUIRE request");
|
||||
const request = require("request");
|
||||
|
||||
console.log("service/cover_excluder INCLUDE notifier");
|
||||
const notifier = require("./notifier");
|
||||
console.log("service/cover_excluder INCLUDE database");
|
||||
const database = require("./database");
|
||||
console.log("service/cover_excluder INCLUDE music_brainz");
|
||||
const music_brainz = require("./music_brainz");
|
||||
console.log("service/cover_excluder INCLUDE the_movie_db");
|
||||
const the_movie_db = require("./the_movie_db");
|
||||
console.log("service/cover_excluder INCLUDE resizer")
|
||||
const resizer = require("../services/cover/resizer")
|
||||
|
||||
|
||||
console.log("service/cover_excluder LOAD server settings")
|
||||
const server = require("../server");
|
||||
const status = server.status;
|
||||
const config = server.config;
|
||||
const cache_folder = config.cache_folder + "/";
|
||||
|
||||
|
||||
console.log("service/cover_excluder SET notifiers")
|
||||
notifier.on("exclude_metadata_finished", () => {
|
||||
process.stdout.write("music scann finished…\n");
|
||||
checkMissingAlbumCovers();
|
||||
checkMissingArtistCovers();
|
||||
status.scanning_music = false;
|
||||
});
|
||||
|
||||
notifier.on("exclude_pathdata_finished", () => {
|
||||
process.stdout.write("video scann finished…\n");
|
||||
checkMissingBoxCovers();
|
||||
status.scanning_video = false;
|
||||
});
|
||||
|
||||
notifier.on("metadata_picture_excluded", album => {
|
||||
resizer.resize_image_for_album(album.picture[0].data, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("cover from metadata…\n");
|
||||
database.albums.updateCovers(album, covers);
|
||||
} else {
|
||||
process.stdout.write("no cover from metadata…\n");
|
||||
music_brainz.find_album_cover(album);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
notifier.on("no_metadata_picture_excluded", album => {
|
||||
music_brainz.find_album_cover(album);
|
||||
});
|
||||
|
||||
notifier.on("found_music_brainz_album_cover", result => {
|
||||
process.stdout.write("MB album result: " + result.mb + "\n");
|
||||
|
||||
let album_id = result.album._id.toString();
|
||||
let tmp_file = cache_folder + album_id;
|
||||
|
||||
let writer = fs.createWriteStream(tmp_file);
|
||||
|
||||
writer.on("finish", () => {
|
||||
resizer.resize_image_for_album(tmp_file, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("album cover from musicbrainz…\n");
|
||||
database.albums.updateCovers(result.album, covers);
|
||||
}
|
||||
fs.unlinkSync(tmp_file);
|
||||
});
|
||||
});
|
||||
|
||||
request(result.mb).pipe(writer);
|
||||
});
|
||||
|
||||
notifier.on("found_music_brainz_artist_cover", result => {
|
||||
process.stdout.write("MB artist result: " + result.mb + "\n");
|
||||
|
||||
let artist_id = result.artist._id.toString();
|
||||
let tmp_file = cache_folder + artist_id;
|
||||
let writer = fs.createWriteStream(tmp_file);
|
||||
|
||||
writer.on("finish", () => {
|
||||
resizer.resize_image_for_artist(tmp_file, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("artist cover from musicbrainz…\n");
|
||||
database.artists.updateCovers(result.artist, covers);
|
||||
}
|
||||
fs.unlinkSync(tmp_file);
|
||||
});
|
||||
});
|
||||
|
||||
request(result.mb).pipe(writer);
|
||||
});
|
||||
|
||||
notifier.on("found_movie_db_box_cover", box => {
|
||||
process.stdout.write("TMVD box result: " + box.tmdb + "\n");
|
||||
|
||||
let box_id = box._id.toString();
|
||||
let tmp_file = cache_folder + box_id;
|
||||
let writer = fs.createWriteStream(tmp_file);
|
||||
|
||||
writer.on("finish", () => {
|
||||
resizer.resize_image_for_box(tmp_file, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("box covers from tmvd…\n");
|
||||
database.boxes.updateCovers(box, covers);
|
||||
}
|
||||
fs.unlinkSync(tmp_file);
|
||||
});
|
||||
});
|
||||
|
||||
request(box.tmdb).pipe(writer);
|
||||
});
|
||||
|
||||
function checkMissingAlbumCovers() {
|
||||
process.stdout.write("started cover excluding for albums…\n");
|
||||
database.albums.collection(-1, null, albums => {
|
||||
albums.forEach(album => {
|
||||
database.albums.tracks(album._id, album_tracks => {
|
||||
if (!album_tracks || !album_tracks.tracks || album.covers) {
|
||||
return;
|
||||
}
|
||||
album.tracks = album_tracks.tracks;
|
||||
let cover_found = false;
|
||||
album.tracks.forEach(track => {
|
||||
if (cover_found) {
|
||||
return;
|
||||
}
|
||||
|
||||
let file_path = config.music_folder + track.path;
|
||||
let directory = path.dirname(file_path);
|
||||
|
||||
config.album_cover_files.forEach(file => {
|
||||
let img = directory + "/" + file;
|
||||
if (fs.existsSync(img)) {
|
||||
cover_found = true;
|
||||
resizer.resize_image_for_album(img, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("covers from file…\n");
|
||||
database.albums.updateCovers(album, covers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!cover_found) {
|
||||
export_from_id3(album);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function export_from_id3(album) {
|
||||
album.tracks.forEach(track => {
|
||||
track.parent = album;
|
||||
});
|
||||
notifier.emit("album_cover_request", album.tracks[0]);
|
||||
}
|
||||
|
||||
function checkMissingArtistCovers() {
|
||||
process.stdout.write("started cover excluding for artists…\n");
|
||||
database.artists.collection(-1, undefined, artists => {
|
||||
artists.forEach(artist => {
|
||||
database.artists.tracks(artist._id.toString(), true, artist_full => {
|
||||
if (!artist_full || !artist_full.albums || artist.covers) {
|
||||
return;
|
||||
}
|
||||
let cover_found = false;
|
||||
artist_full.albums.forEach(album => {
|
||||
album.tracks.forEach(track => {
|
||||
if (cover_found) {
|
||||
return;
|
||||
}
|
||||
config.artist_cover_files.forEach(file => {
|
||||
if (cover_found) {
|
||||
return;
|
||||
}
|
||||
let file_path = config.music_folder + track.path;
|
||||
let directory = path.dirname(file_path);
|
||||
let img = directory + "/" + file;
|
||||
if (fs.existsSync(img)) {
|
||||
cover_found = true;
|
||||
resizer.resize_image_for_artist(img, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("artist covers from file…\n");
|
||||
database.artists.updateCovers(artist, covers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
directory = path.dirname(directory);
|
||||
img = directory + "/" + file;
|
||||
if (fs.existsSync(img)) {
|
||||
cover_found = true;
|
||||
resizer.resize_image_for_artist(img, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("artist covers from file…\n");
|
||||
database.artists.updateCovers(artist, covers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
directory = path.dirname(directory);
|
||||
img = directory + "/" + file;
|
||||
if (fs.existsSync(img)) {
|
||||
cover_found = true;
|
||||
resizer.resize_image_for_artist(img, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("artist covers from file…\n");
|
||||
database.artists.updateCovers(artist, covers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (!cover_found) {
|
||||
music_brainz.find_artist_cover(artist_full);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkMissingBoxCovers() {
|
||||
process.stdout.write("started cover excluding for boxes…\n");
|
||||
database.boxes.collection(-1, undefined, boxes => {
|
||||
boxes.forEach(box => {
|
||||
database.boxes.videos(box._id.toString(), true, box_full => {
|
||||
if (box_full.covers) {
|
||||
return;
|
||||
}
|
||||
let cover_found = false;
|
||||
let directory = config.video_folder + box_full.path;
|
||||
config.box_cover_files.forEach(file => {
|
||||
if (cover_found) {
|
||||
return;
|
||||
}
|
||||
let img = directory + "/" + file;
|
||||
if (fs.existsSync(img)) {
|
||||
cover_found = true;
|
||||
resizer.resize_image_for_box(img, covers => {
|
||||
if (Object.keys(covers).length > 0) {
|
||||
process.stdout.write("box covers from file…\n");
|
||||
database.boxes.updateCovers(box_full, covers);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!cover_found) {
|
||||
the_movie_db.find_box_cover(box);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
25
services/database/CONNECTOR.js
Normal file
25
services/database/CONNECTOR.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { MongoClient } = require('mongodb');
|
||||
const server = require("../../server");
|
||||
const config = server.config;
|
||||
|
||||
var dbo;
|
||||
const url = "mongodb://" + config.database.host + ":" + config.database.port + "/";
|
||||
const database = config.database.name;
|
||||
|
||||
exports.connect = async function () {
|
||||
if (dbo) {
|
||||
console.log("DB CONNECTED")
|
||||
return dbo;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
console.log("DB CONNECTING:" + config.database.host + ":" + config.database.port)
|
||||
const client = await MongoClient.connect(url);
|
||||
dbo = client.db(database);
|
||||
return dbo;
|
||||
} catch (error) {
|
||||
console.error(`MongoDB connection failed with > ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
302
services/database/albums.js
Normal file
302
services/database/albums.js
Normal file
@@ -0,0 +1,302 @@
|
||||
const redis = require("../redis")
|
||||
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("albums").createIndex({ artist_name: 1, year: 1, title: 1 });
|
||||
// TEMPORARY
|
||||
dbo.collection("albums").updateMany({}, { $unset: { cover32: 1, cover64: 1, cover128: 1, cover256: 1, cover512: 1 } });
|
||||
updateArtistName();
|
||||
});
|
||||
|
||||
function updateArtistName() {
|
||||
dbo.collection("albums")
|
||||
.find({ artist_name: undefined })
|
||||
.toArray((err, result) => {
|
||||
result.forEach(item => {
|
||||
dbo.collection("artists")
|
||||
.findOne({ _id: item.artist_id })
|
||||
.then(artist => {
|
||||
if (artist) {
|
||||
dbo.collection("albums")
|
||||
.updateOne(
|
||||
{ _id: item._id },
|
||||
{ $set: { artist_id: artist._id, artist_name: artist.name } },
|
||||
{ upsert: false });
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.collection = function (page, filter, callback) {
|
||||
process.stdout.write("services/db_manager ALBUMS Collection: " + page + "\n");
|
||||
let redis_key = "albumsCollection_" + (filter || '') + '_' + page;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager ALBUMS Collection REDIS: " + page + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
let aggregate = [
|
||||
{ $project: { "covers.cover256": false, "covers.cover64": false, "covers.cover32": false } },
|
||||
{ $match: { visibility: { $not: { $eq: 'hidden' } } } },
|
||||
{ $sort: { artist_name: 1, year: 1, title: 1 } },
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
|
||||
if (page > -1) {
|
||||
let pageSize = 12;
|
||||
let skip = (page - 1) * pageSize;
|
||||
aggregate.push(
|
||||
{ $skip: skip },
|
||||
{ $limit: pageSize });
|
||||
}
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate(aggregate, { allowDiskUse: true })
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(album => {
|
||||
album.type = "album";
|
||||
album.tracks = [];
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager ALBUMS Collection MONGO: " + page + "\n");
|
||||
callback(result);
|
||||
redis.set(redis_key, result);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.favourites = function (id, callback) {
|
||||
dbo.collection("favourites")
|
||||
.find({ userId: id, type: "album" })
|
||||
.toArray((err, favourites) => {
|
||||
if (err) throw err;
|
||||
let aggregate = [
|
||||
{ $match: { _id: { $in: favourites.map(m => m.itemId) } } },
|
||||
{ $project: { "covers.cover256": false, "covers.cover64": false, "covers.cover32": false } },
|
||||
]
|
||||
dbo.collection("albums")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
result.forEach(album => {
|
||||
album.type = "album";
|
||||
album.tracks = [];
|
||||
});
|
||||
callback(result);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
exports.newest = function (count, filter, callback) {
|
||||
let aggregate = [
|
||||
{ $project: { "covers.cover256": false, "covers.cover128": false, "covers.cover32": false } },
|
||||
{ $match: { visibility: { $not: { $eq: 'hidden' } } } },
|
||||
{ $sort: { _id: -1 } },
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
aggregate.push({ $limit: count })
|
||||
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate(aggregate, {
|
||||
allowDiskUse: true
|
||||
})
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(album => {
|
||||
album.type = "album";
|
||||
album.tracks = [];
|
||||
});
|
||||
}
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.byId = function (id, filter, callback) {
|
||||
process.stdout.write("services/db_manager ALBUM by id: " + id + "\n");
|
||||
let redis_key = "albumId_" + (filter || '') + '_' + id;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager ALBUM by id REDIS: " + id + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
let aggregate = [
|
||||
{
|
||||
$lookup: {
|
||||
from: "tracks",
|
||||
localField: "_id",
|
||||
foreignField: "album_id",
|
||||
as: "tracks"
|
||||
}
|
||||
},
|
||||
{ $match: { _id: ObjectId(id) } }
|
||||
]
|
||||
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(album => {
|
||||
album.type = "album";
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager ALBUM by id MONGO: " + id + "\n");
|
||||
callback(result[0]);
|
||||
redis.set(redis_key, result[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.filter = function (term, callback) {
|
||||
let aggregate = [
|
||||
{ $project: { 'parent.covers': false, 'covers.cover32': false, 'covers.cover256': false } },
|
||||
{ $match: { title: { $regex: term, $options: "i" } }, },
|
||||
{ $limit: 6 }
|
||||
]
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.tracks = function (id, callback) {
|
||||
process.stdout.write("services/db_manager TRACKS by id: " + id + "\n");
|
||||
|
||||
let request = [
|
||||
{
|
||||
$lookup: {
|
||||
from: "tracks",
|
||||
localField: "_id",
|
||||
foreignField: "album_id",
|
||||
as: "tracks"
|
||||
}
|
||||
},
|
||||
{ $match: { _id: ObjectId(id) } }
|
||||
];
|
||||
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate(request)
|
||||
.toArray((err, result) => {
|
||||
if (result) {
|
||||
callback(result[0]);
|
||||
} else {
|
||||
if (err) {
|
||||
process.stderr("services/db_manager ALBUM by id ERROR: " + err.message);
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.delete = function (album, callback) {
|
||||
dbo.collection("albums")
|
||||
.deleteOne({ _id: ObjectId(album._id) }, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.update = function (album, callback) {
|
||||
dbo.collection("albums")
|
||||
.updateOne(
|
||||
{ _id: ObjectId(album._id) },
|
||||
{
|
||||
$set: {
|
||||
visibility: album.visibility
|
||||
}
|
||||
},
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
exports.updateCovers = function (album, covers, callback) {
|
||||
dbo.collection("albums")
|
||||
.updateOne(
|
||||
{ _id: ObjectId(album._id) },
|
||||
{ $set: { covers: covers } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.moveTo = function (album, callback) {
|
||||
process.stdout.write("services/db_manager ALBUM '" + album._id + "' move to '" + album.artist_id + "'\n");
|
||||
|
||||
dbo
|
||||
.collection("albums")
|
||||
.updateOne(
|
||||
{ _id: ObjectId(album._id) },
|
||||
{ $set: { artist_id: ObjectId(album.artist_id), artist_name: album.artist_name } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.empty = function (callback) {
|
||||
dbo
|
||||
.collection("albums")
|
||||
.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "tracks",
|
||||
localField: "_id",
|
||||
foreignField: "album_id",
|
||||
as: "tracks"
|
||||
}
|
||||
}
|
||||
])
|
||||
.toArray((err, result) => {
|
||||
callback(result.filter(f => !f.tracks || f.tracks.length == 0));
|
||||
});
|
||||
};
|
||||
264
services/database/artists.js
Normal file
264
services/database/artists.js
Normal file
@@ -0,0 +1,264 @@
|
||||
const redis = require("../redis")
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("artists").createIndex({ name: 1 });
|
||||
// TEMPORARY
|
||||
dbo.collection("artists").updateMany({}, { $unset: { cover32: 1, cover64: 1, cover128: 1, cover256: 1, cover512: 1 } })
|
||||
});
|
||||
|
||||
let artist_lookup_album = {
|
||||
$lookup: {
|
||||
from: "albums",
|
||||
localField: "_id",
|
||||
foreignField: "artist_id",
|
||||
as: "albums"
|
||||
}
|
||||
}
|
||||
let artist_lookup_tracks = {
|
||||
$lookup: {
|
||||
from: "tracks",
|
||||
localField: "albums._id",
|
||||
foreignField: "album_id",
|
||||
as: "albums.tracks"
|
||||
}
|
||||
}
|
||||
let artists_project = {
|
||||
$project: {
|
||||
"covers.cover64": false,
|
||||
"covers.cover128": false,
|
||||
"covers.cover512": false,
|
||||
"covers.cover1024": false,
|
||||
}
|
||||
}
|
||||
let artist_project = {
|
||||
$project: {
|
||||
"albums.tracks.path": false,
|
||||
"albums.tracks.bitrate": false,
|
||||
"albums.tracks.album_id": false,
|
||||
"albums.tracks.mime": false,
|
||||
"albums.artist_id": false,
|
||||
"albums.covers.cover256": false,
|
||||
"albums.covers.cover512": false
|
||||
}
|
||||
}
|
||||
exports.collection = function (page, filter, callback) {
|
||||
process.stdout.write("services/db_manager ARTISTS Collection: " + page + "\n");
|
||||
let redis_key = "artistsCollection_" + (filter || '') + '_' + page;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager ARTISTS Collection REDIS: " + page + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
|
||||
let aggregate = []
|
||||
if (filter) {
|
||||
aggregate.push(
|
||||
artist_lookup_album, {
|
||||
$match: { "albums.visibility": { $in: filter } }
|
||||
});
|
||||
}
|
||||
aggregate.push(
|
||||
artists_project,
|
||||
{ $sort: { name: 1 } }
|
||||
)
|
||||
if (page > -1) {
|
||||
let pageSize = 12;
|
||||
let skip = (page - 1) * pageSize;
|
||||
aggregate.push(
|
||||
{ $skip: skip },
|
||||
{ $limit: pageSize });
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("artists")
|
||||
.aggregate(aggregate, { allowDiskUse: true })
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
item.type = "artist";
|
||||
item.albums = [];
|
||||
item.tracks = [];
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager ARTISTS Collection MONGO: " + page + "\n");
|
||||
callback(result);
|
||||
redis.set(redis_key, result);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.favourites = function (id, callback) {
|
||||
dbo.collection("favourites")
|
||||
.find({ userId: id, type: "artist" })
|
||||
.toArray((err, favourites) => {
|
||||
if (err) throw err;
|
||||
let aggregate = [
|
||||
{ $match: { _id: { $in: favourites.map(m => m.itemId) } } },
|
||||
artists_project,
|
||||
]
|
||||
dbo.collection("artists")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
result.forEach(item => {
|
||||
item.type = "artist";
|
||||
item.albums = [];
|
||||
item.tracks = [];
|
||||
});
|
||||
callback(result);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
exports.byId = function (id, filter, callback) {
|
||||
process.stdout.write("services/db_manager ARTIST by id: " + id + "\n");
|
||||
let redis_key = "artistId_" + (filter || '') + '_' + id;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager ARTIST by id REDIS: " + id + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
|
||||
let aggregate = [
|
||||
artist_lookup_album,
|
||||
{
|
||||
$unwind: { path: "$albums" }
|
||||
},
|
||||
artist_lookup_tracks,
|
||||
artist_project,
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
name: { $first: "$name" },
|
||||
covers: { $first: "$covers" },
|
||||
albums: { $push: "$albums" }
|
||||
}
|
||||
},
|
||||
{ $match: { _id: ObjectId(id) } },
|
||||
];
|
||||
dbo
|
||||
.collection("artists")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
item.type = "artist";
|
||||
item.tracks = [];
|
||||
if (filter) {
|
||||
item.albums = item.albums.filter(album => { return filter.indexOf(album.visibility) > -1 });
|
||||
}
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager ARTIST by id MONGO: " + id + "\n");
|
||||
callback(result[0]);
|
||||
redis.set(redis_key, result[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.filter = function (term, callback) {
|
||||
let aggregate = [
|
||||
{ $project: { 'covers.cover64': false, 'covers.cover512': false } },
|
||||
{ $match: { name: { $regex: term, $options: "i" } }, },
|
||||
{ $limit: 6 }
|
||||
]
|
||||
dbo
|
||||
.collection("artists")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.tracks = function (id, showPath, callback) {
|
||||
let request = [
|
||||
artist_lookup_album,
|
||||
{
|
||||
$unwind: { path: "$albums" }
|
||||
},
|
||||
artist_lookup_tracks,
|
||||
{ $match: { _id: ObjectId(id) } },
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
name: { $first: "$name" },
|
||||
albums: { $push: "$albums" }
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (!showPath) {
|
||||
request.push({
|
||||
$project: {
|
||||
"albums.tracks.path": false,
|
||||
"albums.tracks.bitrate": false,
|
||||
"albums.tracks.album_id": false,
|
||||
"albums.tracks.mime": false,
|
||||
"albums.artist_id": false,
|
||||
"albums.covers": false,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("artists")
|
||||
.aggregate(request)
|
||||
.toArray((err, result) => {
|
||||
if (result) {
|
||||
callback(result[0]);
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.delete = function (artist, callback) {
|
||||
dbo.collection("artists")
|
||||
.deleteOne({ _id: ObjectId(artist._id) }, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateCovers = function (artist, covers, callback) {
|
||||
dbo.collection("artists").updateOne(
|
||||
{ _id: ObjectId(artist._id) },
|
||||
{ $set: { covers: covers } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.empty = function (callback) {
|
||||
dbo
|
||||
.collection("artists")
|
||||
.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "albums",
|
||||
localField: "_id",
|
||||
foreignField: "artist_id",
|
||||
as: "albums"
|
||||
}
|
||||
}
|
||||
])
|
||||
.toArray((err, result) => {
|
||||
callback(result.filter(f => !f.albums || f.albums.length == 0));
|
||||
});
|
||||
};
|
||||
273
services/database/boxes.js
Normal file
273
services/database/boxes.js
Normal file
@@ -0,0 +1,273 @@
|
||||
const redis = require("../redis")
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("boxes").createIndex({ title: 1 });
|
||||
// TEMPORARY
|
||||
dbo.collection("boxes").updateMany({}, { $unset: { cover32: 1, cover64: 1, cover128: 1, cover256: 1, cover512: 1 } })
|
||||
});
|
||||
|
||||
let box_project = {
|
||||
$project: {
|
||||
"videos.path": false,
|
||||
"videos.box_id": false,
|
||||
"videos.mime": false,
|
||||
path: false,
|
||||
"covers.cover32": false,
|
||||
}
|
||||
}
|
||||
let boxes_project = {
|
||||
$project: {
|
||||
path: false,
|
||||
"covers.cover32": false,
|
||||
"covers.cover64": false,
|
||||
"covers.cover256": false
|
||||
}
|
||||
}
|
||||
let box_lookup_videos = {
|
||||
$lookup: {
|
||||
from: "videos",
|
||||
localField: "_id",
|
||||
foreignField: "box_id",
|
||||
as: "videos"
|
||||
}
|
||||
}
|
||||
|
||||
exports.collection = function (page, filter, callback) {
|
||||
process.stdout.write("services/db_manager BOXES Collection: " + page + "\n");
|
||||
let redis_key = "boxesCollection_" + (filter || '') + '_' + page;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager BOXES Collection REDIS: " + page + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
|
||||
let aggregate = [
|
||||
boxes_project,
|
||||
{ $match: { visibility: { $not: { $eq: 'hidden' } } } },
|
||||
{ $sort: { title: 1 } },
|
||||
];
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
if (page > -1) {
|
||||
let pageSize = 12;
|
||||
let skip = (page - 1) * pageSize;
|
||||
aggregate.push(
|
||||
{ $skip: skip },
|
||||
{ $limit: pageSize });
|
||||
}
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate(aggregate, {
|
||||
allowDiskUse: true
|
||||
})
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
item.type = "box";
|
||||
item.videos = [];
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager BOXES Collection MONGO: " + page + "\n");
|
||||
callback(result);
|
||||
redis.set(redis_key, result);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.favourites = function (id, callback) {
|
||||
dbo.collection("favourites")
|
||||
.find({ userId: id, type: "box" })
|
||||
.toArray((err, favourites) => {
|
||||
if (err) throw err;
|
||||
let aggregate = [
|
||||
{ $match: { _id: { $in: favourites.map(m => m.itemId) } } },
|
||||
boxes_project,
|
||||
]
|
||||
dbo.collection("boxes")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
result.forEach(item => {
|
||||
item.type = "box";
|
||||
item.videos = [];
|
||||
});
|
||||
callback(result);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
exports.newest = function (count, filter, callback) {
|
||||
let aggregate = [
|
||||
boxes_project,
|
||||
{ $sort: { _id: -1 } },
|
||||
{ $match: { visibility: { $not: { $eq: 'hidden' } } } }
|
||||
];
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
aggregate.push({ $limit: count })
|
||||
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate(aggregate, {
|
||||
allowDiskUse: true
|
||||
})
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
item.type = "box";
|
||||
item.videos = [];
|
||||
});
|
||||
}
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports.byId = function (id, filter, callback) {
|
||||
process.stdout.write("services/db_manager BOX by id: " + id + "\n");
|
||||
let redis_key = "boxId_" + (filter || '') + '_' + id;
|
||||
|
||||
redis.get(redis_key, (value) => {
|
||||
if (value) {
|
||||
process.stdout.write("services/db_manager BOX by id REDIS: " + id + "\n");
|
||||
callback(value);
|
||||
} else {
|
||||
let aggregate = [
|
||||
box_lookup_videos,
|
||||
box_project,
|
||||
{ $match: { _id: ObjectId(id) } }
|
||||
];
|
||||
if (filter) {
|
||||
aggregate.push({
|
||||
$match: { visibility: { $in: filter } }
|
||||
});
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
result.forEach(item => {
|
||||
item.type = "box";
|
||||
});
|
||||
}
|
||||
process.stdout.write("services/db_manager BOX by id MONGO: " + id + "\n");
|
||||
callback(result[0]);
|
||||
redis.set(redis_key, result[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.filter = function (term, callback) {
|
||||
let aggregate = [
|
||||
{ $project: { 'covers.cover256': false } },
|
||||
{ $match: { title: { $regex: term, $options: "i" } }, },
|
||||
{ $limit: 6 }
|
||||
]
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate(aggregate)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.empty = function (callback) {
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate([box_lookup_videos])
|
||||
.toArray((err, result) => {
|
||||
callback(result.filter(f => !f.videos || f.videos.length == 0));
|
||||
});
|
||||
};
|
||||
|
||||
exports.videos = function (id, showPath, callback) {
|
||||
let request = [
|
||||
{
|
||||
$lookup: {
|
||||
from: "videos",
|
||||
localField: "_id",
|
||||
foreignField: "box_id",
|
||||
as: "videos"
|
||||
}
|
||||
},
|
||||
{ $match: { _id: ObjectId(id) } }
|
||||
];
|
||||
|
||||
if (!showPath) {
|
||||
request.push({
|
||||
$project: {
|
||||
"videos.mime": false,
|
||||
"videos.path": false,
|
||||
"videos.box_id": false,
|
||||
path: false,
|
||||
"covers.cover32": false,
|
||||
"covers.cover64": false,
|
||||
"covers.cover128": false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.aggregate(request)
|
||||
.toArray((err, result) => {
|
||||
callback(result[0]);
|
||||
});
|
||||
};
|
||||
|
||||
exports.delete = function (box, callback) {
|
||||
dbo.collection("boxes").deleteOne({ _id: box._id }, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
exports.update = function (box, callback) {
|
||||
dbo.collection("boxes").updateOne(
|
||||
{ _id: ObjectId(box._id) },
|
||||
{
|
||||
$set: {
|
||||
visibility: box.visibility
|
||||
}
|
||||
},
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
exports.updateCovers = function (box, covers, callback) {
|
||||
dbo.collection("boxes").updateOne(
|
||||
{ _id: ObjectId(box._id) },
|
||||
{ $set: { covers: covers } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
734
services/database/index.js
Normal file
734
services/database/index.js
Normal file
@@ -0,0 +1,734 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const bcrypt = require("bcryptjs");
|
||||
const ffmpeg = require("fluent-ffmpeg");
|
||||
const fs = require("fs");
|
||||
|
||||
const notifier = require("../notifier");
|
||||
|
||||
const server = require("../../server");
|
||||
const config = server.config;
|
||||
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
});
|
||||
|
||||
notifier.on("metadata_excluded", item => {
|
||||
insert_artist_if_not_exists(item, artist => {
|
||||
insert_album_if_not_exists(item, artist, album => {
|
||||
insert_track_if_not_exists(item, album);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
notifier.on("pathdata_excluded", item => {
|
||||
insert_box_if_not_exists(item, box => {
|
||||
insert_video_if_not_exists(item, box);
|
||||
});
|
||||
});
|
||||
|
||||
notifier.on("check_video_details", item => {
|
||||
if (!item.tracks) {
|
||||
get_video_tracks(item, tracks => {
|
||||
item.tracks = tracks;
|
||||
update_video(item);
|
||||
});
|
||||
}
|
||||
if (!item.thumbnail) {
|
||||
get_video_thumbnail(item, thumbnail => {
|
||||
item.thumbnail = thumbnail;
|
||||
update_video(item);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
MODULES
|
||||
*/
|
||||
|
||||
const albums = require("./albums");
|
||||
exports.albums = albums;
|
||||
|
||||
const radios = require("./radios");
|
||||
exports.radios = radios;
|
||||
|
||||
const tracks = require("./tracks");
|
||||
exports.tracks = tracks;
|
||||
|
||||
const artists = require("./artists");
|
||||
exports.artists = artists;
|
||||
|
||||
const boxes = require("./boxes");
|
||||
exports.boxes = boxes;
|
||||
|
||||
const videos = require("./videos");
|
||||
exports.videos = videos;
|
||||
|
||||
const users = require("./users");
|
||||
exports.users = users;
|
||||
|
||||
const system = require("./system");
|
||||
exports.system = system;
|
||||
|
||||
|
||||
exports.artist_count = function (callback) {
|
||||
return dbo.collection("artists").countDocuments(callback);
|
||||
};
|
||||
|
||||
exports.album_count = function (callback) {
|
||||
return dbo.collection("albums").countDocuments(callback);
|
||||
};
|
||||
|
||||
exports.track_count = function (callback) {
|
||||
return dbo.collection("tracks").countDocuments(callback);
|
||||
};
|
||||
|
||||
exports.box_count = function (callback) {
|
||||
return dbo.collection("boxes").countDocuments(callback);
|
||||
};
|
||||
|
||||
exports.video_count = function (callback) {
|
||||
return dbo.collection("videos").countDocuments(callback);
|
||||
};
|
||||
|
||||
exports.users_count = function (callback) {
|
||||
return dbo.collection("users").countDocuments(callback);
|
||||
};
|
||||
|
||||
/*
|
||||
USER
|
||||
*/
|
||||
|
||||
exports.addUser = function (user, callback) {
|
||||
bcrypt.genSalt(10, function (err, salt) {
|
||||
bcrypt.hash(user.password, salt, function (err, hash) {
|
||||
user.password = hash;
|
||||
if (!user.roles) {
|
||||
user.roles = ["user"];
|
||||
}
|
||||
user.name = user.name.toLowerCase();
|
||||
user.player = {};
|
||||
user.fullname = user.name;
|
||||
dbo.collection("users").insertOne(user, err => {
|
||||
if (err) throw err;
|
||||
dbo.collection("users").findOne({ name: user.name }, (err, result) => {
|
||||
if (err) throw err;
|
||||
delete result.password;
|
||||
callback(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.deleteUser = function (id, callback) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.deleteOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateUserRole = function (user, callback) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.updateOne({ name: user.name }, { $set: { roles: user.roles } }, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateUserAccess = function (name) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.updateOne(
|
||||
{ name: name },
|
||||
{ $set: { last_access: new Date().toLocaleString() } }
|
||||
);
|
||||
};
|
||||
|
||||
exports.updateUserPassword = function (user, newPassowrd) {
|
||||
bcrypt.genSalt(10, function (err, salt) {
|
||||
bcrypt.hash(newPassowrd, salt, function (err, hash) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.updateOne({ name: user }, { $set: { password: hash } },
|
||||
err => { if (err) throw err; });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateUserConfig = function (user, body, callback) {
|
||||
dbo.collection("users").updateOne(
|
||||
{ name: user.name },
|
||||
{
|
||||
$set: {
|
||||
mobile_bpm: body.mobile_bpm,
|
||||
desktop_bpm: body.desktop_bpm,
|
||||
video_lang: body.video_lang,
|
||||
video_quality: body.video_quality,
|
||||
fullname: body.fullname
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.updateUserSettings = function (user, callback) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.updateOne(
|
||||
{ _id: user._id },
|
||||
{ $set: { player: user.player } },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.userByName = function (name, callback) {
|
||||
dbo.collection("users").findOne({ name: name.toLowerCase() }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.userById = function (id, callback) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.findOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
HISTORY
|
||||
*/
|
||||
exports.historyList = function (id, callback) {
|
||||
dbo
|
||||
.collection("history")
|
||||
.find(
|
||||
{ userId: id, type: { $in: ['album', 'artist', 'box', 'radio'] } },
|
||||
{ projection: { _id: false, userId: false } }
|
||||
)
|
||||
.sort({ _id: -1 })
|
||||
.limit(12)
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.clearHistory = function (id, callback) {
|
||||
dbo
|
||||
.collection("history")
|
||||
.deleteMany({ userId: id }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateHistory = function (item, callback) {
|
||||
dbo
|
||||
.collection("history")
|
||||
.deleteMany({ userId: item.userId, id: item.id, type: { $in: ['album', 'artist', 'box', 'radio'] } }, err => {
|
||||
if (err) throw err;
|
||||
dbo
|
||||
.collection("history")
|
||||
.insertOne(item, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
exports.search = function (term, callback) {
|
||||
let r = [];
|
||||
dbo
|
||||
.collection("artists")
|
||||
.find(
|
||||
{ name: { $regex: term, $options: "i" } },
|
||||
{ projection: { _id: true, name: true, "covers.cover256": true } }
|
||||
)
|
||||
.toArray((err, result) => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "artist";
|
||||
item.search_rank = item.name.toLowerCase().indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
dbo
|
||||
.collection("albums")
|
||||
.find(
|
||||
{ title: { $regex: term, $options: "i" } },
|
||||
{ projection: { _id: true, title: true, "covers.cover128": true } }
|
||||
)
|
||||
.toArray((err, result) => {
|
||||
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "album";
|
||||
item.parent = {};
|
||||
item.search_rank = item.title.toLowerCase().indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
dbo
|
||||
.collection("tracks")
|
||||
.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "albums",
|
||||
localField: "album_id",
|
||||
foreignField: "_id",
|
||||
as: "parent"
|
||||
}
|
||||
}, {
|
||||
$unwind: "$parent"
|
||||
}, {
|
||||
$project: {
|
||||
album_id: false,
|
||||
bitrate: false,
|
||||
disk: false,
|
||||
duration: false,
|
||||
mime: false,
|
||||
path: false,
|
||||
genre: false,
|
||||
track: false,
|
||||
"parent.artist_id": false,
|
||||
"parent.year": false,
|
||||
"parent.covers.cover32": false,
|
||||
"parent.covers.cover128": false,
|
||||
"parent.covers.cover256": false,
|
||||
"parent.covers.cover512": false
|
||||
}
|
||||
}, {
|
||||
$match: { title: { $regex: term, $options: "i" } }
|
||||
}])
|
||||
.toArray((err, result) => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "track";
|
||||
item.search_rank = item.title.toLowerCase().indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.find(
|
||||
{ title: { $regex: term, $options: "i" } },
|
||||
{ projection: { _id: true, title: true, "covers.cover128": true } }
|
||||
)
|
||||
.toArray((err, result) => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "box";
|
||||
item.search_rank = item.title.toLowerCase().indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("videos")
|
||||
.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "boxes",
|
||||
localField: "box_id",
|
||||
foreignField: "_id",
|
||||
as: "parent"
|
||||
}
|
||||
}, {
|
||||
$unwind: "$parent"
|
||||
}, {
|
||||
$project: {
|
||||
path: false,
|
||||
mime: false,
|
||||
tracks: false,
|
||||
box_id: false,
|
||||
"parent.box_id": false,
|
||||
"parent.year": false,
|
||||
"parent.path": false,
|
||||
"parent.title": false,
|
||||
"parent.covers.cover32": false,
|
||||
"parent.covers.cover64": false,
|
||||
"parent.covers.cover128": false,
|
||||
"parent.covers.cover256": false,
|
||||
"parent.covers.cover512": false
|
||||
}
|
||||
}, {
|
||||
$match: { title: { $regex: term, $options: "i" } }
|
||||
}]
|
||||
)
|
||||
.toArray((err, result) => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "video";
|
||||
item.search_rank = item.title
|
||||
.toLowerCase()
|
||||
.indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
dbo
|
||||
.collection("radios")
|
||||
.find(
|
||||
{ name: { $regex: term, $options: "i" } },
|
||||
{ projection: { _id: true, name: true } }
|
||||
)
|
||||
.toArray((err, result) => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach(item => {
|
||||
item.type = "radio";
|
||||
item.search_rank = item.name
|
||||
.toLowerCase()
|
||||
.indexOf(term);
|
||||
});
|
||||
r = r.concat(result);
|
||||
}
|
||||
callback(
|
||||
r
|
||||
.sort((a, b) => {
|
||||
return a.search_rank - b.search_rank;
|
||||
})
|
||||
.splice(0, 50)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
CHAT
|
||||
*/
|
||||
exports.messageList = function (callback) {
|
||||
dbo
|
||||
.collection("chat")
|
||||
.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: "users",
|
||||
localField: "user_id",
|
||||
foreignField: "_id",
|
||||
as: "user"
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
"user.password": false,
|
||||
"user.player": false
|
||||
}
|
||||
},
|
||||
{ $unwind: "$user" },
|
||||
{ $sort: { _id: 1 } }
|
||||
])
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result.splice(0, 100));
|
||||
});
|
||||
};
|
||||
|
||||
exports.writeMessage = function (user, message, callback) {
|
||||
dbo
|
||||
.collection("chat")
|
||||
.insertOne(
|
||||
{ user_id: user._id, message: message, timestamp: Date.now() },
|
||||
(err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result.ops);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.deleteMessage = function (id, user, callback) {
|
||||
dbo.collection("chat").deleteOne({ _id: ObjectId(id), user_id: user._id }, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
FUNCTIONS
|
||||
*/
|
||||
function insert_artist_if_not_exists(item, callback) {
|
||||
dbo.collection("artists").findOne({ name: item.artist }, (err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
callback(result);
|
||||
} else {
|
||||
let artist = {
|
||||
name: item.artist,
|
||||
insert_on: Date.now()
|
||||
}
|
||||
if (item.owner) {
|
||||
artist.owner_id = item.owner._id;
|
||||
}
|
||||
dbo
|
||||
.collection("artists")
|
||||
.updateOne(
|
||||
{ name: item.artist },
|
||||
{ $set: artist },
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
insert_artist_if_not_exists(item, callback);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function insert_album_if_not_exists(item, artist, callback) {
|
||||
dbo
|
||||
.collection("albums")
|
||||
.findOne({ artist_id: artist._id, title: item.album }, (err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
callback(result);
|
||||
} else {
|
||||
let album = {
|
||||
artist_id: artist._id,
|
||||
artist_name: artist.name,
|
||||
title: item.album.trim(),
|
||||
year: item.year,
|
||||
insert_on: Date.now()
|
||||
}
|
||||
if (item.covers) {
|
||||
album.cover32 = item.covers.cover32;
|
||||
album.cover64 = item.covers.cover64;
|
||||
album.cover128 = item.covers.cover128;
|
||||
album.cover256 = item.covers.cover256;
|
||||
}
|
||||
if (item.owner) {
|
||||
album.owner_id = item.owner._id;
|
||||
}
|
||||
dbo.collection("albums").updateOne(
|
||||
{ artist_id: artist._id, title: item.album },
|
||||
{ $set: album },
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
insert_album_if_not_exists(item, artist, callback);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function insert_track_if_not_exists(item, album, callback) {
|
||||
let short_path = ""
|
||||
if (item.path.indexOf(config.upload_folder) == 0) {
|
||||
short_path = item.path.replace(config.upload_folder, "");
|
||||
} else {
|
||||
short_path = item.path.replace(config.music_folder, "");
|
||||
}
|
||||
|
||||
dbo.collection("tracks").updateOne(
|
||||
{ path: short_path },
|
||||
{
|
||||
$set: {
|
||||
title: item.title,
|
||||
path: short_path,
|
||||
album_id: album._id,
|
||||
duration: item.duration,
|
||||
bitrate: item.bitrate,
|
||||
mime: item.mime,
|
||||
disk: item.disk,
|
||||
track: item.track,
|
||||
genre: item.genre,
|
||||
insert_on: Date.now()
|
||||
}
|
||||
},
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
dbo.collection("tracks").findOne({ path: short_path }, (err, result) => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function insert_box_if_not_exists(item, callback) {
|
||||
let short_path = item.box_path.replace(config.video_folder, "");
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.findOne({ title: item.box, year: item.year }, (err, result) => {
|
||||
if (err) throw err;
|
||||
if (result) {
|
||||
callback(result);
|
||||
} else {
|
||||
let box = {
|
||||
title: item.box,
|
||||
path: short_path,
|
||||
year: item.year,
|
||||
cover64: item.cover64,
|
||||
cover128: item.cover128,
|
||||
cover256: item.cover256,
|
||||
cover512: item.cover512,
|
||||
insert_on: Date.now()
|
||||
}
|
||||
if (item.owner) {
|
||||
box.owner_id = item.owner._id;
|
||||
}
|
||||
dbo
|
||||
.collection("boxes")
|
||||
.updateOne(
|
||||
{ title: item.box },
|
||||
{ $set: box },
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
insert_box_if_not_exists(item, callback);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function insert_video_if_not_exists(item, box, callback) {
|
||||
item.path = item.path.replace(config.video_folder, "");
|
||||
dbo.collection("videos").updateOne(
|
||||
{ path: item.path },
|
||||
{
|
||||
$set: {
|
||||
title: item.title,
|
||||
path: item.path,
|
||||
box_id: box._id,
|
||||
mime: item.mime,
|
||||
insert_on: Date.now()
|
||||
}
|
||||
},
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
dbo.collection("videos").findOne({ path: item.path }, (err, result) => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
if (!result.tracks) {
|
||||
get_video_tracks(item, tracks => {
|
||||
result.tracks = tracks;
|
||||
update_video(result);
|
||||
});
|
||||
}
|
||||
if (!result.thumbnail) {
|
||||
get_video_thumbnail(result, thumbnail => {
|
||||
result.thumbnail = thumbnail;
|
||||
update_video(result);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function update_video(video, callback) {
|
||||
process.stdout.write("updating video: " + video.title + "\n");
|
||||
let val = {};
|
||||
if (video.tracks) {
|
||||
val.tracks = video.tracks;
|
||||
}
|
||||
if (video.thumbnail) {
|
||||
val.thumbnail = video.thumbnail;
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dbo
|
||||
.collection("videos")
|
||||
.updateOne({ _id: video._id }, { $set: val }, { upsert: false }, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_video_thumbnail(video, callback) {
|
||||
let path = config.video_folder + video.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
path = config.upload_folder + video.path;
|
||||
}
|
||||
ffmpeg(path)
|
||||
.on("end", () => {
|
||||
process.stdout.write("Thumbnail for '" + video.title + "' created\n");
|
||||
let thumbnail =
|
||||
config.cache_folder + "/video_covers/" + video._id.toString() + ".png";
|
||||
if (!fs.existsSync(thumbnail)) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
let bitmap = fs.readFileSync(thumbnail);
|
||||
callback("data:image/png;base64, " + bitmap.toString("base64"));
|
||||
fs.unlinkSync(thumbnail);
|
||||
})
|
||||
.on("error", err => {
|
||||
process.stdout.write(
|
||||
"Thumbnail error for " + video.title + ": " + err.message + "\n"
|
||||
);
|
||||
callback(null);
|
||||
})
|
||||
.screenshot({
|
||||
timestamps: ["20%"],
|
||||
filename: video._id.toString(),
|
||||
folder: config.cache_folder + "/video_covers",
|
||||
size: "346x180"
|
||||
});
|
||||
}
|
||||
|
||||
function get_video_tracks(video, callback) {
|
||||
let path = config.video_folder + video.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
path = config.upload_folder + video.path;
|
||||
}
|
||||
ffmpeg(path).ffprobe((err, data) => {
|
||||
if (!data) {
|
||||
return callback([]);
|
||||
}
|
||||
let audio_streams = data.streams.filter(f => f.codec_type == "audio");
|
||||
let return_value = audio_streams.map(m => {
|
||||
return {
|
||||
index: m.index,
|
||||
lang: (m.tags && m.tags.language
|
||||
? m.tags.language
|
||||
: m.index
|
||||
? m.index
|
||||
: ""
|
||||
).toString(),
|
||||
title: (m.tags && m.tags.title
|
||||
? m.tags.title
|
||||
: m.index
|
||||
? m.index
|
||||
: ""
|
||||
).toString()
|
||||
};
|
||||
});
|
||||
callback(return_value);
|
||||
});
|
||||
}
|
||||
79
services/database/radios.js
Normal file
79
services/database/radios.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
});
|
||||
|
||||
exports.collection = function (callback) {
|
||||
dbo
|
||||
.collection("radios")
|
||||
.find({})
|
||||
.sort({ name: 1 })
|
||||
.toArray((err, result) => {
|
||||
result.forEach(item => {
|
||||
item.type = "radio";
|
||||
});
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.byId = function (id, callback) {
|
||||
dbo
|
||||
.collection("radios")
|
||||
.findOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.add = function (radio, callback) {
|
||||
dbo.collection("radios").updateOne(
|
||||
{ url: radio.url },
|
||||
{
|
||||
$set: {
|
||||
name: radio.name,
|
||||
url: radio.url
|
||||
}
|
||||
},
|
||||
{ upsert: true },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
dbo.collection("radios").findOne({ url: radio.url }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.delete = function (id, callback) {
|
||||
dbo
|
||||
.collection("radios")
|
||||
.deleteOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.update = function (radio, callback) {
|
||||
dbo.collection("radios").updateOne(
|
||||
{ _id: radio._id },
|
||||
{
|
||||
$set: {
|
||||
name: radio.name,
|
||||
url: radio.url,
|
||||
cover32: radio.cover32,
|
||||
cover64: radio.cover64,
|
||||
cover128: radio.cover128
|
||||
}
|
||||
},
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
59
services/database/system.js
Normal file
59
services/database/system.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
});
|
||||
|
||||
exports.domains = function (callback) {
|
||||
dbo
|
||||
.collection("system")
|
||||
.findOne({ key: 'domains' })
|
||||
.then(result => {
|
||||
if (result) {
|
||||
callback(result.value);
|
||||
} else {
|
||||
callback([]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.allows = function (callback) {
|
||||
dbo.collection("system")
|
||||
.findOne({ key: 'allows' })
|
||||
.then(allows => {
|
||||
if (!allows) {
|
||||
callback({
|
||||
"guests": false,
|
||||
"register": false,
|
||||
});
|
||||
} else {
|
||||
callback(allows.value);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
exports.setAllows = function (allows, callback) {
|
||||
dbo.collection("system")
|
||||
.updateOne(
|
||||
{ key: "allows" },
|
||||
{ $set: { value: allows } },
|
||||
{ upsert: true }, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.setDomains = function (domains, callback) {
|
||||
dbo.collection("system")
|
||||
.updateOne(
|
||||
{ key: "domains" },
|
||||
{ $set: { value: domains } },
|
||||
{ upsert: true }, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
113
services/database/tracks.js
Normal file
113
services/database/tracks.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("tracks").createIndex({ path: 1, album_id: 1, "disk.no": 1, "track.no": 1 });
|
||||
});
|
||||
|
||||
const server = require("../../server");
|
||||
const config = server.config;
|
||||
|
||||
exports.collection = function (callback) {
|
||||
dbo
|
||||
.collection("tracks")
|
||||
.find()
|
||||
.toArray((err, result) => {
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.byId = function (id, callback) {
|
||||
dbo
|
||||
.collection("tracks")
|
||||
.findOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.delete = function (track, callback) {
|
||||
dbo.collection("tracks").deleteOne(track, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
exports.exists = function (path, callback) {
|
||||
dbo
|
||||
.collection("tracks")
|
||||
.find({ path: path.replace(config.music_folder, "") })
|
||||
.limit(1)
|
||||
.toArray((err, result) => {
|
||||
callback(result.length > 0);
|
||||
});
|
||||
};
|
||||
|
||||
exports.moveTo = function (track, callback) {
|
||||
dbo
|
||||
.collection("tracks")
|
||||
.updateOne(
|
||||
{ _id: ObjectId(track._id) },
|
||||
{ $set: { album_id: ObjectId(track.album_id) } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.mostListened = function (filter, callback) {
|
||||
let aggregate = [{
|
||||
$group: {
|
||||
_id: "$id",
|
||||
title: { $last: "$title" },
|
||||
covers: { $last: "$covers" },
|
||||
parent: { $last: "$parent" },
|
||||
counter: { $sum: 1 },
|
||||
album: { $last: "$album" }
|
||||
},
|
||||
}]
|
||||
if (filter) {
|
||||
aggregate
|
||||
.unshift({
|
||||
$lookup: {
|
||||
from: "albums",
|
||||
let: { album_id: "$parent._id" },
|
||||
pipeline: [{
|
||||
$match: {
|
||||
$expr: {
|
||||
$eq: ["$_id", { $toObjectId: "$$album_id" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
as: "album"
|
||||
}
|
||||
}, { $unwind: "$album" })
|
||||
aggregate
|
||||
.push({
|
||||
$project: {
|
||||
"album.cover256": false,
|
||||
"album.cover128": false,
|
||||
"album.cover64": false,
|
||||
"album.cover32": false
|
||||
}
|
||||
}, { $match: { "album.visibility": { $in: filter } } });
|
||||
} else {
|
||||
aggregate.unshift({ $match: { type: 'track' } });
|
||||
}
|
||||
aggregate.push({ $sort: { counter: -1, _id: -1 } }, { $limit: 6 })
|
||||
|
||||
dbo
|
||||
.collection("history")
|
||||
.aggregate(aggregate, {
|
||||
allowDiskUse: true
|
||||
})
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
49
services/database/users.js
Normal file
49
services/database/users.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("favourites").createIndex({ userId: 1 });
|
||||
});
|
||||
|
||||
exports.collection = function (callback) {
|
||||
dbo
|
||||
.collection("users")
|
||||
.find({}, { password: false })
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.favourites = function (id, callback) {
|
||||
dbo.collection("favourites")
|
||||
.find({ userId: id })
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
}
|
||||
|
||||
exports.insertFavourite = function (item, callback) {
|
||||
item.itemId = ObjectId(item.itemId);
|
||||
dbo
|
||||
.collection("favourites")
|
||||
.insertOne(item, err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.deleteFavourite = function (item, callback) {
|
||||
dbo
|
||||
.collection("favourites")
|
||||
.deleteMany({ userId: item.userId, itemId: ObjectId(item.itemId) }, (err) => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
118
services/database/videos.js
Normal file
118
services/database/videos.js
Normal file
@@ -0,0 +1,118 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
var connector = require("./CONNECTOR");
|
||||
var dbo;
|
||||
connector.connect().then((ret) => {
|
||||
dbo = ret;
|
||||
dbo.collection("videos").createIndex({ box_id: 1, title: 1 });
|
||||
});
|
||||
|
||||
var server = require("../../server");
|
||||
var config = server.config;
|
||||
|
||||
exports.exists = function (path, callback) {
|
||||
let short_path = path.replace(config.video_folder, "");
|
||||
dbo.collection("videos").findOne({ path: short_path }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.collection = function (callback) {
|
||||
dbo
|
||||
.collection("videos")
|
||||
.find()
|
||||
.toArray((err, result) => {
|
||||
result.forEach(item => {
|
||||
item.thumbnail = "";
|
||||
});
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.byId = function (id, callback) {
|
||||
dbo
|
||||
.collection("videos")
|
||||
.findOne({ _id: ObjectId(id) }, (err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
|
||||
exports.delete = function (video, callback) {
|
||||
dbo.collection("videos")
|
||||
.deleteOne({ _id: ObjectId(video._id) }, err => {
|
||||
if (err) throw err;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
exports.moveTo = function (video, callback) {
|
||||
dbo
|
||||
.collection("videos")
|
||||
.updateOne(
|
||||
{ _id: ObjectId(video._id) },
|
||||
{ $set: { box_id: ObjectId(video.box_id) } },
|
||||
{ upsert: false },
|
||||
err => {
|
||||
if (err) throw err;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.mostViewed = function (filter, callback) {
|
||||
let aggregate = [{
|
||||
$group: {
|
||||
_id: "$id",
|
||||
title: { $last: "$title" },
|
||||
thumbnail: { $last: "$thumbnail" },
|
||||
parent: { $last: "$parent" },
|
||||
counter: { $sum: 1 },
|
||||
box: { $last: "$box" }
|
||||
},
|
||||
}]
|
||||
if (filter) {
|
||||
aggregate
|
||||
.unshift({
|
||||
$lookup: {
|
||||
from: "boxes",
|
||||
let: { box_id: "$parent._id" },
|
||||
pipeline: [{
|
||||
$match: {
|
||||
$expr: {
|
||||
$eq: ["$_id", { $toObjectId: "$$box_id" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
as: "box"
|
||||
}
|
||||
}, { $unwind: "$box" })
|
||||
aggregate.push({
|
||||
$project: {
|
||||
"box.cover256": false,
|
||||
"box.cover128": false,
|
||||
"box.cover64": false,
|
||||
"box.cover32": false,
|
||||
"box.path": false
|
||||
}
|
||||
}, { $match: { "box.visibility": { $in: filter } } });
|
||||
}
|
||||
else {
|
||||
aggregate.unshift({ $match: { type: 'video' } });
|
||||
}
|
||||
aggregate.push(
|
||||
{ $sort: { counter: -1, _id: -1 } },
|
||||
{ $limit: 12 });
|
||||
|
||||
dbo
|
||||
.collection("history")
|
||||
.aggregate(aggregate, {
|
||||
allowDiskUse: true
|
||||
})
|
||||
.toArray((err, result) => {
|
||||
if (err) throw err;
|
||||
callback(result);
|
||||
});
|
||||
};
|
||||
135
services/files_scanner.js
Normal file
135
services/files_scanner.js
Normal file
@@ -0,0 +1,135 @@
|
||||
var fs = require("fs");
|
||||
var mime = require("mime-types");
|
||||
var notifier = require("./notifier");
|
||||
var database = require("./database");
|
||||
|
||||
var server = require("../server");
|
||||
var config = server.config;
|
||||
|
||||
/*
|
||||
MUSIC
|
||||
*/
|
||||
|
||||
exports.scann_local_music_files = async function (path) {
|
||||
remove_non_exists_tracks();
|
||||
scann_local_music_files(path);
|
||||
};
|
||||
|
||||
async function remove_non_exists_tracks() {
|
||||
database.tracks.collection(tracks => {
|
||||
tracks.forEach(track => {
|
||||
let path = config.music_folder + track.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
path = config.upload_folder + track.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
database.tracks.delete(track, () => {
|
||||
notifier.emit("track_deleted", track._id);
|
||||
process.stdout.write("track deleted: " + path + "\n");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
process.stdout.write("check empty albums\n");
|
||||
database.albums.empty(albums => {
|
||||
albums.forEach(album => {
|
||||
database.albums.delete(album, () => {
|
||||
notifier.emit("album_deleted", album._id);
|
||||
process.stdout.write("album deleted: " + album.title + "\n");
|
||||
});
|
||||
});
|
||||
process.stdout.write("check empty artists\n");
|
||||
database.artists.empty(artists => {
|
||||
artists.forEach(artist => {
|
||||
database.artists.delete(artist, () => {
|
||||
notifier.emit("artist_deleted", artist._id);
|
||||
process.stdout.write("artist deleted: " + artist.name + "\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function scann_local_music_files(path) {
|
||||
if (!fs.existsSync(path)) {
|
||||
return;
|
||||
}
|
||||
fs.readdirSync(path).forEach(child => {
|
||||
if (!child.startsWith(".")) {
|
||||
var full_path = path + "/" + child;
|
||||
if (fs.lstatSync(full_path).isDirectory()) {
|
||||
//setTimeout(() => {
|
||||
scann_local_music_files(full_path);
|
||||
//}, 1000);
|
||||
} else {
|
||||
var mime_type = mime.lookup(full_path);
|
||||
if (
|
||||
mime_type &&
|
||||
mime_type.startsWith("audio/") &&
|
||||
mime_type.indexOf("x-mpegurl") == -1 &&
|
||||
mime_type.indexOf("x-scpls") == -1
|
||||
) {
|
||||
let item = { path: full_path, mime: mime_type };
|
||||
notifier.emit("music_file_found", item);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
VIDEO
|
||||
*/
|
||||
|
||||
exports.scann_local_video_files = async function (path) {
|
||||
remove_non_exists_videos();
|
||||
scann_local_video_files(path);
|
||||
};
|
||||
|
||||
async function remove_non_exists_videos() {
|
||||
database.videos.collection(videos => {
|
||||
videos.forEach(video => {
|
||||
let path = config.video_folder + video.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
path = config.upload_folder + video.path;
|
||||
if (!fs.existsSync(path)) {
|
||||
database.videos.delete(video, () => {
|
||||
notifier.emit("video_deleted", video._id);
|
||||
process.stdout.write("video deleted: " + path + "\n");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
process.stdout.write("check empty boxes\n");
|
||||
database.boxes.empty(boxes => {
|
||||
boxes.forEach(box => {
|
||||
database.boxes.delete(box, () => {
|
||||
notifier.emit("box_deleted", box._id);
|
||||
process.stdout.write("box deleted: " + box.title + "\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function scann_local_video_files(path) {
|
||||
if (!fs.existsSync(path)) {
|
||||
return;
|
||||
}
|
||||
fs.readdirSync(path).forEach(child => {
|
||||
if (!child.startsWith(".")) {
|
||||
var full_path = path + "/" + child;
|
||||
if (fs.lstatSync(full_path).isDirectory()) {
|
||||
//setTimeout(() => {
|
||||
scann_local_video_files(full_path);
|
||||
//}, 1000);
|
||||
} else {
|
||||
var mime_type = mime.lookup(full_path);
|
||||
if (mime_type && mime_type.startsWith("video/")) {
|
||||
let item = { path: full_path, mime: mime_type };
|
||||
notifier.emit("video_file_found", item);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
215
services/music_brainz.js
Normal file
215
services/music_brainz.js
Normal file
@@ -0,0 +1,215 @@
|
||||
var request = require("request");
|
||||
var notifier = require("./notifier");
|
||||
|
||||
var album_cover_queue = [];
|
||||
var album_cover_requst_is_running = false;
|
||||
|
||||
var artist_cover_queue = [];
|
||||
var artist_cover_queue_is_running = false;
|
||||
|
||||
let options = {
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"WebPlay/0.1.0 (https://gitea.com/WebPlay)"
|
||||
}
|
||||
};
|
||||
|
||||
exports.find_album_cover = function(album) {
|
||||
album_cover_queue.push(album);
|
||||
console.log("MB Album: " + album.title + " by " + album.artist_name);
|
||||
run_album_cover_request();
|
||||
};
|
||||
|
||||
exports.find_artist_cover = function(artist) {
|
||||
artist_cover_queue.push(artist);
|
||||
console.log("MB Artist: " + artist.name);
|
||||
run_artist_cover_request();
|
||||
};
|
||||
|
||||
// ARTIST COVER
|
||||
async function run_artist_cover_request() {
|
||||
console.log("started request for artist covers");
|
||||
if (artist_cover_queue_is_running) {
|
||||
return;
|
||||
}
|
||||
artist_cover_queue_is_running = true;
|
||||
while (artist_cover_queue && artist_cover_queue.length > 0) {
|
||||
await sleep(1500);
|
||||
let artist = artist_cover_queue.shift();
|
||||
console.log("SHIFT Artist: " + artist.name);
|
||||
for (let i = 0; i < artist.albums.length; i++) {
|
||||
await sleep(1500);
|
||||
if (artist.image_downloaded) {
|
||||
break;
|
||||
}
|
||||
let album = artist.albums[i];
|
||||
let album_title = album.title.replace("&", "%26").replace("/", "_");
|
||||
let artist_name = artist.name.replace("&", "%26").replace("/", "_");
|
||||
let url = `https://musicbrainz.org/ws/2/release/?query=release:${album_title} AND artist:${artist_name}&fmt=json`;
|
||||
|
||||
options.url = url;
|
||||
request(options, (err, res, body) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
let json = json_parser(body, url);
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
if (json.releases && json.releases.length > 0) {
|
||||
let release = json.releases[0];
|
||||
if (release["artist-credit"] && release["artist-credit"].length > 0) {
|
||||
let artist_id = release["artist-credit"][0].artist.id;
|
||||
console.log(artist_id);
|
||||
get_image_by_artist_id(artist, artist_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
artist_cover_queue_is_running = false;
|
||||
}
|
||||
|
||||
async function get_image_by_artist_id(artist, artist_id) {
|
||||
let url = `https://musicbrainz.org/ws/2/artist/${artist_id}?inc=url-rels&fmt=json`;
|
||||
console.log(url);
|
||||
options.url = url;
|
||||
request(options, (err, res, body) => {
|
||||
let json = json_parser(body, url);
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
if (!json.relations) {
|
||||
return;
|
||||
}
|
||||
|
||||
json.relations.forEach(relation => {
|
||||
if (relation.type == "image" && relation.url && relation.url.resource) {
|
||||
let resource = relation.url.resource;
|
||||
if (resource.includes("commons.wikimedia.org")) {
|
||||
get_image_by_wikimedia(artist, resource);
|
||||
} else {
|
||||
artist.image_downloaded = true;
|
||||
notifier.emit("found_music_brainz_artist_cover", {
|
||||
artist: artist,
|
||||
mb: resource
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_image_by_wikimedia(artist, url) {
|
||||
let regex = RegExp("(?<=File:)[^<]*", "g");
|
||||
let result = regex.exec(url);
|
||||
if (result) {
|
||||
console.log(result[0]);
|
||||
let file = result[0];
|
||||
let url = `https://en.wikipedia.org/w/api.php?action=query&titles=File:${file}&prop=imageinfo&iiprop=url&iiurlwidth=600&iiurlheight=600&format=json`;
|
||||
console.log(url);
|
||||
options.url = url;
|
||||
request(options, (err, res, body) => {
|
||||
let json = json_parser(body, url);
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.query.pages["-1"].imageinfo[0].thumburl) {
|
||||
console.log(json.query.pages["-1"].imageinfo[0].thumburl);
|
||||
artist.image_downloaded = true;
|
||||
notifier.emit("found_music_brainz_artist_cover", {
|
||||
artist: artist,
|
||||
mb: json.query.pages["-1"].imageinfo[0].thumburl
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ALBUM COVER
|
||||
async function run_album_cover_request() {
|
||||
console.log("started request for album covers");
|
||||
if (album_cover_requst_is_running) {
|
||||
return;
|
||||
}
|
||||
album_cover_requst_is_running = true;
|
||||
while (album_cover_queue && album_cover_queue.length > 0) {
|
||||
await sleep(1500);
|
||||
let album = album_cover_queue.shift();
|
||||
console.log("SHIFT Album: " + album.title);
|
||||
let album_title = album.title.replace("&", "%26").replace("/", "_");
|
||||
let artist_name = album.artist_name.replace("&", "%26").replace("/", "_");
|
||||
|
||||
let url = `https://musicbrainz.org/ws/2/release/?query=release:${album_title} AND artist:${artist_name}&fmt=json`;
|
||||
console.log(url);
|
||||
|
||||
options.url = url;
|
||||
request(options, (err, res, body) => {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(body);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (json.releases && json.releases.length > 0) {
|
||||
let release_id = json.releases[0].id;
|
||||
let title = json.releases[0].title;
|
||||
console.log(release_id + ": " + title);
|
||||
get_album_cover_url(album, release_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
album_cover_requst_is_running = false;
|
||||
}
|
||||
|
||||
function get_album_cover_url(album, id) {
|
||||
let url = "https://coverartarchive.org/release/" + id;
|
||||
options.url = url;
|
||||
request(options, (err, res, body) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
let json = JSON.parse(body);
|
||||
if (json.images && json.images.length > 0) {
|
||||
if (json.images[0].thumbnails.large) {
|
||||
notifier.emit("found_music_brainz_album_cover", {
|
||||
album: album,
|
||||
mb: json.images[0].thumbnails.large
|
||||
});
|
||||
} else if (json.images[0].image) {
|
||||
notifier.emit("found_music_brainz_album_cover", {
|
||||
album: album,
|
||||
mb: json.images[0].image
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
}
|
||||
|
||||
function json_parser(body, url) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(body);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(url);
|
||||
console.log("BODY============BEGIN");
|
||||
console.log(body);
|
||||
console.log("BODY============END");
|
||||
}
|
||||
return json;
|
||||
}
|
||||
4
services/notifier.js
Normal file
4
services/notifier.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var events = require('events');
|
||||
var notifier = new events.EventEmitter();
|
||||
|
||||
module.exports = notifier;
|
||||
36
services/redis/index.js
Normal file
36
services/redis/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { createClient } = require('redis');
|
||||
const server = require("../../server");
|
||||
const config = server.config;
|
||||
|
||||
const redisUrl = "redis://" + config.redis.host + ":" + config.redis.port
|
||||
|
||||
const client = createClient({
|
||||
url: redisUrl
|
||||
});
|
||||
|
||||
client.on('error', (err) => console.log('Redis Client Error', err));
|
||||
|
||||
client.connect();
|
||||
client.flushAll();
|
||||
|
||||
const expire = 57600; // 24h
|
||||
|
||||
exports.set = function (key, value) {
|
||||
if (value) {
|
||||
client.set(key, JSON.stringify(value));
|
||||
client.expire(key, expire);
|
||||
}
|
||||
}
|
||||
|
||||
exports.get = function (key, callback) {
|
||||
process.stdout.write("services/redis get '" + key + "'\n");
|
||||
client.get(key).then(value => {
|
||||
callback(JSON.parse(value));
|
||||
});
|
||||
client.expire(key, expire);
|
||||
}
|
||||
|
||||
exports.flushAll = function () {
|
||||
client.flushAll();
|
||||
process.stdout.write("services/redis flushAll()\n");
|
||||
}
|
||||
262
services/tag_excluder.js
Normal file
262
services/tag_excluder.js
Normal file
@@ -0,0 +1,262 @@
|
||||
var path = require("path");
|
||||
var notifier = require("./notifier");
|
||||
var meta_data = require("music-metadata");
|
||||
var db_manager = require("./database");
|
||||
var server = require("../server");
|
||||
var config = server.config;
|
||||
|
||||
var music_files_data = [];
|
||||
var music_files_cover = [];
|
||||
var parsing_music_data = 0;
|
||||
var parsing_cover = 0;
|
||||
|
||||
var video_files_data = [];
|
||||
var parsing_video_data = 0;
|
||||
|
||||
var exclude_meta_cover_is_running = false;
|
||||
var exclude_meta_data_is_running = false;
|
||||
var exclude_path_data_is_running = false;
|
||||
|
||||
var sleep_dur = 10;
|
||||
|
||||
exports.parsing_music_data = function() {
|
||||
return parsing_music_data;
|
||||
};
|
||||
|
||||
exports.parsing_video_data = function() {
|
||||
return parsing_video_data;
|
||||
};
|
||||
|
||||
exports.parsing_cover = function() {
|
||||
return parsing_cover;
|
||||
};
|
||||
|
||||
var music_timeout_handler_data;
|
||||
var music_timeout_handler_cover;
|
||||
var video_timeout_handler_data;
|
||||
|
||||
exports.get_buffer_size = () => {
|
||||
return music_files_data.length;
|
||||
};
|
||||
|
||||
notifier.on("music_file_found", file => {
|
||||
music_files_data.push(file);
|
||||
if (music_timeout_handler_data) {
|
||||
clearTimeout(music_timeout_handler_data);
|
||||
}
|
||||
music_timeout_handler_data = setTimeout(exclude_meta_data, 1000);
|
||||
});
|
||||
|
||||
notifier.on("album_cover_request", track => {
|
||||
music_files_cover.push(track);
|
||||
if (music_timeout_handler_cover) {
|
||||
clearTimeout(music_timeout_handler_cover);
|
||||
}
|
||||
music_timeout_handler_cover = setTimeout(exclude_meta_cover, 1000);
|
||||
});
|
||||
|
||||
async function exclude_meta_data() {
|
||||
if (exclude_meta_data_is_running) {
|
||||
return;
|
||||
}
|
||||
process.stdout.write("Start excluding meta data...\n");
|
||||
exclude_meta_data_is_running = true;
|
||||
|
||||
while (music_files_data && music_files_data.length > 0) {
|
||||
while (parsing_music_data >= 1) {
|
||||
await sleep(sleep_dur);
|
||||
}
|
||||
parsing_music_data++;
|
||||
let file = music_files_data.shift();
|
||||
if (!file) {
|
||||
parsing_music_data--;
|
||||
continue;
|
||||
}
|
||||
|
||||
db_manager.tracks.exists(file.path, exists => {
|
||||
if (exists) {
|
||||
parsing_music_data--;
|
||||
} else {
|
||||
let options = {
|
||||
skipCovers: true,
|
||||
skipPostHeaders: true,
|
||||
duration: true
|
||||
};
|
||||
meta_data
|
||||
.parseFile(file.path, options)
|
||||
.then(meta => {
|
||||
let title = (meta.common.title || "").trim();
|
||||
let album = (meta.common.album || "").trim();
|
||||
let artist = (
|
||||
meta.common.albumartist ||
|
||||
meta.common.artist ||
|
||||
""
|
||||
).trim();
|
||||
|
||||
if (title == "") {
|
||||
let ext = path.extname(file.path);
|
||||
title = path.basename(file.path, ext);
|
||||
}
|
||||
|
||||
if (album == "") {
|
||||
let dir = path.dirname(file.path);
|
||||
album = path.basename(dir);
|
||||
}
|
||||
|
||||
let item = {
|
||||
path: file.path,
|
||||
artist: artist,
|
||||
album: album,
|
||||
title: title,
|
||||
duration: meta.format.duration,
|
||||
bitrate: meta.format.bitrate,
|
||||
mime: file.mime,
|
||||
year: meta.common.year,
|
||||
track: meta.common.track,
|
||||
disk: meta.common.disk,
|
||||
genre: meta.common.genre
|
||||
};
|
||||
notifier.emit("metadata_excluded", item);
|
||||
parsing_music_data--;
|
||||
})
|
||||
.catch(err => {
|
||||
process.stdout.write(file.path);
|
||||
console.log(err);
|
||||
parsing_music_data--;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.write("End excluding meta data...\n");
|
||||
exclude_meta_data_is_running = false;
|
||||
|
||||
notifier.emit("exclude_metadata_finished");
|
||||
}
|
||||
|
||||
async function exclude_meta_cover() {
|
||||
if (exclude_meta_cover_is_running) {
|
||||
return;
|
||||
}
|
||||
process.stdout.write("Start excluding meta COVER...\n");
|
||||
exclude_meta_cover_is_running = true;
|
||||
|
||||
while (music_files_cover && music_files_cover.length > 0) {
|
||||
while (parsing_cover >= server.config.cpu_cores) {
|
||||
await sleep(50);
|
||||
}
|
||||
parsing_cover++;
|
||||
let track = music_files_cover.shift();
|
||||
if (!track) {
|
||||
parsing_cover--;
|
||||
process.stdout.write("file is null");
|
||||
continue;
|
||||
}
|
||||
let path = config.music_folder + track.path;
|
||||
meta_data
|
||||
.parseFile(path)
|
||||
.then(meta => {
|
||||
parsing_cover--;
|
||||
if (meta.common.picture) {
|
||||
track.parent.picture = meta.common.picture;
|
||||
notifier.emit("metadata_picture_excluded", track.parent);
|
||||
} else {
|
||||
notifier.emit("no_metadata_picture_excluded", track.parent);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
music_files_cover.splice(0, 1);
|
||||
parsing_cover--;
|
||||
notifier.emit("no_metadata_picture_excluded", track.parent);
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
exclude_meta_cover_is_running = false;
|
||||
process.stdout.write("End excluding meta COVER...\n");
|
||||
|
||||
notifier.emit("exclude_metacover_finished");
|
||||
}
|
||||
|
||||
notifier.on("video_file_found", file => {
|
||||
video_files_data.push(file);
|
||||
if (video_timeout_handler_data) {
|
||||
clearTimeout(video_timeout_handler_data);
|
||||
}
|
||||
video_timeout_handler_data = setTimeout(exclude_path_data, 1000);
|
||||
});
|
||||
|
||||
async function exclude_path_data() {
|
||||
if (exclude_path_data_is_running) {
|
||||
return;
|
||||
}
|
||||
process.stdout.write("Start excluding path data...\n");
|
||||
exclude_path_data_is_running = true;
|
||||
|
||||
while (video_files_data && video_files_data.length > 0) {
|
||||
while (parsing_video_data >= 1) {
|
||||
await sleep(sleep_dur);
|
||||
}
|
||||
|
||||
parsing_video_data++;
|
||||
let video = video_files_data.shift();
|
||||
if (!video) {
|
||||
parsing_video_data--;
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = video.path;
|
||||
db_manager.videos.exists(file_path, exists => {
|
||||
if (exists) {
|
||||
parsing_video_data--;
|
||||
if (!exists.thumbnail || !exists.tracks) {
|
||||
notifier.emit("check_video_details", exists);
|
||||
}
|
||||
} else {
|
||||
let dir = path.dirname(file_path);
|
||||
let box = path.basename(dir).trim();
|
||||
let year = 0;
|
||||
|
||||
let regex_year = /\s?\([12]\d\d\d\)$/g;
|
||||
let regex_season = /^seasons?\s*\d+$/gi;
|
||||
let result = regex_season.exec(box);
|
||||
if (result) {
|
||||
let season_dir = path.dirname(dir);
|
||||
let parent = path.basename(season_dir);
|
||||
box = parent + " - " + box;
|
||||
} else {
|
||||
process.stdout.write("check box title: " + box + "\n");
|
||||
result = regex_year.exec(box);
|
||||
if (result) {
|
||||
box = box.replace(result[0], "");
|
||||
year = result[0]
|
||||
.replace("(", "")
|
||||
.replace(")", "")
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
|
||||
let ext = path.extname(file_path);
|
||||
let title = path.basename(file_path, ext);
|
||||
|
||||
let item = {
|
||||
path: file_path,
|
||||
box: box,
|
||||
box_path: dir,
|
||||
title: title,
|
||||
mime: video.mime,
|
||||
year: year
|
||||
};
|
||||
notifier.emit("pathdata_excluded", item);
|
||||
parsing_video_data--;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.write("End excluding path data...\n");
|
||||
exclude_path_data_is_running = false;
|
||||
notifier.emit("exclude_pathdata_finished");
|
||||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
}
|
||||
146
services/the_movie_db.js
Normal file
146
services/the_movie_db.js
Normal file
@@ -0,0 +1,146 @@
|
||||
var request = require("request");
|
||||
var notifier = require("./notifier");
|
||||
|
||||
var box_cover_queue = [];
|
||||
var box_cover_requst_is_running = false;
|
||||
|
||||
var key = "3f3692d9c336994625a838a95b9a2ed0";
|
||||
var poster_root = "https://image.tmdb.org/t/p/w500";
|
||||
|
||||
let options = {
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"WebPlay/0.1.0 (https://gitea.com/WebPlay)"
|
||||
}
|
||||
};
|
||||
|
||||
exports.find_box_cover = function (box) {
|
||||
box_cover_queue.push(box);
|
||||
process.stdout.write("TMDB Box: " + box.title + "\n");
|
||||
run_box_cover_request();
|
||||
};
|
||||
|
||||
async function run_box_cover_request() {
|
||||
process.stdout.write("started request for box covers\n");
|
||||
if (box_cover_requst_is_running) {
|
||||
return;
|
||||
}
|
||||
box_cover_requst_is_running = true;
|
||||
while (box_cover_queue && box_cover_queue.length > 0) {
|
||||
await sleep(1500);
|
||||
let box = box_cover_queue.shift();
|
||||
process.stdout.write("SHIFT box: " + box.title + "\n");
|
||||
|
||||
let elements = check_for_season(box.title);
|
||||
|
||||
if (elements.season > 0) {
|
||||
process.stdout.write("Has SEASONS: " + box.title)
|
||||
cover_by_season_number(box, elements);
|
||||
} else {
|
||||
process.stdout.write("Has NO SEASONS: " + box.title)
|
||||
cover_by_movie_title(box);
|
||||
}
|
||||
}
|
||||
box_cover_requst_is_running = false;
|
||||
}
|
||||
|
||||
function cover_by_movie_title(box) {
|
||||
options.url = `https://api.themoviedb.org/3/search/movie?api_key=${key}&query=${box.title.replace(' ', '%20')}&page=1&include_adult=false`;
|
||||
if (box.year > 0) {
|
||||
options.url = `https://api.themoviedb.org/3/search/movie?api_key=${key}&query=${box.title}&page=1&primary_release_year=${box.year}&include_adult=false`;
|
||||
}
|
||||
|
||||
process.stdout.write("\nSTART: " + options.url + "\n");
|
||||
try {
|
||||
request(options, (err, res, body) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (res.statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
process.stdout.write("PARSE BODY FOR: " + box.title + "\n");
|
||||
process.stdout.write(body);
|
||||
process.stdout.write("\n\n");
|
||||
|
||||
let json = JSON.parse(body);
|
||||
if (
|
||||
json.results &&
|
||||
json.results.length > 0 &&
|
||||
json.results[0].poster_path
|
||||
) {
|
||||
box.tmdb = poster_root + json.results[0].poster_path;
|
||||
notifier.emit("found_movie_db_box_cover", box);
|
||||
}
|
||||
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
process.stdout.write("\nERROR: " + box.title + "\n");
|
||||
process.stdout.write(options.url + "\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
function cover_by_season_number(box, elements) {
|
||||
options.url = `https://api.themoviedb.org/3/search/tv?api_key=${key}&query=${elements.title
|
||||
}&page=1`;
|
||||
|
||||
request(options, (err, res, body) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (res.statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
let json = JSON.parse(body);
|
||||
|
||||
if (json.results && json.results.length > 0) {
|
||||
let id = json.results[0].id;
|
||||
cover_by_tv_id(id, elements.season, box);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cover_by_tv_id(id, season, box) {
|
||||
options.url = `https://api.themoviedb.org/3/tv/${id}/season/${season}?api_key=${key}`;
|
||||
|
||||
request(options, (err, res, body) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (res.statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
let json = JSON.parse(body);
|
||||
if (json.poster_path) {
|
||||
box.tmdb = poster_root + json.poster_path;
|
||||
notifier.emit("found_movie_db_box_cover", box);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function check_for_season(title) {
|
||||
let return_value = { title: title, season: 0 };
|
||||
let regex_season = /\s*\-?\s*seasons?\s*\d+$/gi;
|
||||
|
||||
let result = regex_season.exec(title);
|
||||
if (result) {
|
||||
return_value.title = title.replace(result[0], "");
|
||||
|
||||
let regex_season_no = /\d*$/g;
|
||||
result = regex_season_no.exec(title);
|
||||
return_value.season = parseInt(result[0]);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
}
|
||||
Reference in New Issue
Block a user