{"size": 17836, "ext": "js", "max_stars_repo_path": "routes/track-routes.js", "max_stars_repo_name": "bahaaEldeen1999/Marioplex-Backend", "max_stars_repo_stars_event_min_datetime": "2020-03-13T14:07:09.000Z", "max_stars_repo_stars_event_max_datetime": "2021-02-12T15:06:49.000Z", "max_issues_repo_path": "routes/track-routes.js", "max_issues_repo_name": "bahaaEldeen1999/Marioplex-Backend", "max_issues_repo_issues_event_min_datetime": "2021-05-11T17:02:05.000Z", "max_issues_repo_issues_event_max_datetime": "2022-01-22T12:35:27.000Z", "max_forks_repo_path": "routes/track-routes.js", "max_forks_repo_name": "MohmedMonsef/Marioplex-Backend", "max_forks_repo_forks_event_min_datetime": "2020-05-01T22:16:18.000Z", "max_forks_repo_forks_event_max_datetime": "2021-05-06T03:53:53.000Z"}
{"max_stars_count": 8, "max_issues_count": 2, "max_forks_count": 8, "avg_line_length": 42.0660377358, "max_line_length": 147, "alphanum_fraction": 0.5423301189}
const router = require('express').Router();
const Track = require('../source/track-api');
const User = require('../source/user-api');
const jwt = require('jsonwebtoken');
const jwtSecret = require('../config/jwt-key').secret;
const { auth: checkAuth } = require('../middlewares/is-me');
const { auth: checkIfAuth } = require('../middlewares/check-if-auth');

const rateLimit = require('express-rate-limit');
// add rate limiting
const limiter = rateLimit({
    windowMs: 60 * 1000,
    max: 30

});

router.get('/me/track/:track_id', checkIfAuth, limiter, async(req, res) => {
    try {
        if (req.isAuth) {
            const trackId = req.params.track_id;
            let user = await User.getUserById(req.user._id);
            const track = await Track.getTrack(trackId, user);
            if (!track) res.status(404).json({ 'error': 'track not found' }); //not found
            else res.json(track);
        } else {

            const trackId = req.params.track_id;
            const track = await Track.getTrackWithoutAuth(trackId);
            if (!track) res.status(404).json({ 'error': 'track not found' }); //not found
            else res.json(track);
        }
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// get track with some user info as like
router.get('/track/:track_id', checkIfAuth, limiter, async(req, res) => {
        try {
            if (req.isAuth) {

                const trackId = req.params.track_id;
                const user = await User.getUserById(req.user._id);
                if (!user) { res.status(403).json({ 'error': 'user not allowed' }); return; }
                const fullTrack = await Track.getFullTrack(trackId, user);
                if (!fullTrack) { res.status(404).json({ 'error': 'track not found' }); return; }
                // if all are found return them in new created json object
                res.json(fullTrack);
            } else {

                const trackId = req.params.track_id;
                const track = await Track.getTrackWithoutAuth(trackId);
                if (!track) res.sendStatus(404); //not found
                else res.json(track);
            }
        } catch (ex) {
            res.status(400).send({ 'error': 'error in making the request' });
        }
    })
    // get tracks
router.get('/tracks/', checkAuth, limiter, async(req, res) => {
    try {
        if (req.body.ids) {

            const user = await User.getUserById(req.user._id);
            const trackIds = req.body.ids ? req.body.ids.split(',') : [];
            const tracks = await Track.getTracks(trackIds, user);
            if (tracks.length == 0) res.status(404).send({ error: 'tracks with those id\'s are not found' });
            else res.json(tracks);
        } else res.status(404).send({ error: 'tracks id\'s are required' });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// get track audio feature/analysis
router.get('/track/audio-features/:track_id', checkAuth, limiter, async(req, res) => {
    try {
        const audioFeature = await Track.getAudioFeaturesTrack(req.params.track_id);
        if (!audioFeature) res.status(404).send({ error: 'no track with this id' });
        else res.json(audioFeature);
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// get tracks audio feature/analysis 
router.get('/tracks/audio-features/', checkAuth, limiter, async(req, res) => {
    try {
        if (req.body.ids) {
            const trackIds = req.body.ids ? req.body.ids.split(',') : [];
            const audioFeatures = await Track.getAudioFeaturesTracks(trackIds);
            if (!audioFeatures) res.status(404).send({ error: 'no tracks with this id' });
            else res.json(audioFeatures);
        } else res.status(400).send({ error: 'tracks id\'s are required' });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// user like track
router.put('/me/like/:track_id', checkAuth, limiter, async(req, res) => {
    try {

        const userId = req.user._id; // get it from desierialize auth 
        const trackId = req.params.track_id;
        const updatedUser = await User.likeTrack(userId, trackId);
        if (!updatedUser) res.status(405).send({ error: 'already liked the song' }); // if user already liked the song
        else res.send({ success: 'liked the song successfully' });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
});

// user unlike track
router.delete('/me/unlike/:track_id', checkAuth, limiter, async(req, res) => {
    try {
        const userId = req.user._id; // get it from desierialize auth
        const trackId = req.params.track_id;
        const updatedUser = await User.unlikeTrack(userId, trackId);

        if (!updatedUser) res.status(405).send({ error: 'user didnt liked the song before' }); // if user already liked the song
        else res.send({ success: 'unliked the song successfully' });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
});

// set android route which will just serve webm media file


router.get('/tracks/android/:track_id', checkAuth, limiter, async(req, res) => {
        try {
            let type = req.query.type; // high low medium or review
            let user = await User.getUserById(req.user._id);
            if (type != 'review') {
                // if not premium and user asked for high quality then send it as low
                if (req.user.product == 'free' && type == 'high') type = 'medium'
                    // set default quality to medium if not specified

                if (type != 'high' || type != 'medium' || type != 'low' || type != 'review') type = 'medium';
            }
            const trackId = req.params.track_id;
            const track = await Track.getTrack(trackId, user);
            if (!track) {
                res.status(404).json({ 'error': 'no track found with this id' });
                return 0;
            }


            // get track id from google drive list
            drive.files.list({
                corpora: 'user',
                pageSize: 10,
                fields: 'files(*)',
                q: `(appProperties  has {   key='trackId' and value='${trackId}' }) and (appProperties  has {   key='type' and value='${type}' }) `

            }, (err, data) => {
                if (err) { res.status(404).send('no data'); return; }
                if (data.data.files.length == 0) { res.status(404).send('no data'); return; }
                const id = data.data.files[0].id;
                console.log(id)
                drive.files.get({ fileId: id, alt: 'media' }, { responseType: 'stream' },
                    function(err, file) {
                        if (err || !file) { res.status(404).send('no data'); return; }
                        const mimType = file.headers['content-type'];
                        const length = Number(file.headers['content-length']);
                        const range = req.headers.range;
                        if (range) {
                            console.log('range')
                            var parts = req.headers['range'].replace(/bytes=/, '').split('-');
                            var partialstart = parts[0];
                            var partialend = parts[1];

                            var start = parseInt(partialstart, 10);
                            var end = partialend ? parseInt(partialend, 10) : length - 1;
                            var chunksize = (end - start) + 1;
                            res.writeHead(206, {
                                'Content-Range': 'bytes ' + start + '-' + end + '/' + length,
                                'Accept-Ranges': 'bytes',
                                'Content-Length': chunksize,
                                'Content-Type': mimType
                            });
                            file.data.on('end', () => { console.log('Done'); }).on('error', err => { console.log('Error', err); }).pipe(res);
                        } else {
                            // if doesnt support range then send it sequential using pipe method in nodejs
                            res.header('Content-Length', length);
                            res.header('Content-Type', mimType);

                            file.data.on('end', () => { console.log('Done'); }).on('error', err => { console.log('Error', err); }).pipe(res);
                        }

                    });
            });

        } catch (ex) {
            res.status(400).send({ 'error': 'error in making the request' });
        }

    })
    // set web player route which will just serve encrypted webm media file
router.get('/tracks/web-player/:track_id', limiter, async(req, res) => {
    try {
        let type = req.query.type; // high low medium or review
        // get token as query parameter
        const token = req.query.token;

        if (!token) { return res.status(401).send('No Available token'); return 0; }

        try {
            const decoded = jwt.verify(token, jwtSecret);
            req.user = decoded;

        } catch (ex) {

            return res.status(400).send('Invalid Token');
        }

        // if not premium and user asked for high quality then send it as low
        if (req.user.product == 'free' && type == 'high') type = 'medium'

        // set default quality to medium if not specified
        if (type != 'high' || type != 'medium' || type != 'low') type = 'medium';
        let user = await User.getUserById(req.user._id);
        const trackId = req.params.track_id;
        const track = await Track.getTrack(trackId, user);
        if (!track) {
            res.status(404).json({ 'error': 'no track found with this id' });
            return 0;
        }

        // get track id from google drive list
        drive.files.list({
            corpora: 'user',
            pageSize: 10,
            fields: 'files(*)',
            q: `(appProperties  has {   key='trackId' and value='${trackId}' }) and (appProperties  has {   key='type' and value='${type}_enc' }) `

        }, (err, data) => {
            if (err) { res.status(404).send('no data'); return; }
            if (data.data.files.length == 0) { res.status(404).send('no data'); return; }
            const id = data.data.files[0].id;
            console.log(id)
            drive.files.get({ fileId: id, alt: 'media' }, { responseType: 'stream' },
                function(err, file) {
                    if (err || !file) { res.status(404).send('no data'); return; }
                    const mimType = file.headers['content-type'];
                    const length = Number(file.headers['content-length']);
                    const range = req.headers.range;
                    if (range) {
                        console.log('range')
                        var parts = req.headers['range'].replace(/bytes=/, '').split('-');
                        var partialstart = parts[0];
                        var partialend = parts[1];

                        var start = parseInt(partialstart, 10);
                        var end = partialend ? parseInt(partialend, 10) : length - 1;
                        var chunksize = (end - start) + 1;
                        res.writeHead(206, {
                            'Content-Range': 'bytes ' + start + '-' + end + '/' + length,
                            'Accept-Ranges': 'bytes',
                            'Content-Length': chunksize,
                            'Content-Type': mimType
                        });
                        file.data.on('end', () => { console.log('Done'); }).on('error', err => { console.log('Error', err); }).pipe(res);
                    } else {
                        // if doesnt support range then send it sequential using pipe method in nodejs
                        res.header('Content-Length', length);
                        res.header('Content-Type', mimType);

                        file.data.on('end', () => { console.log('Done'); }).on('error', err => { console.log('Error', err); }).pipe(res);
                    }

                });
        });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// set the route to get the track encryption key to decrypt the track
router.get('/tracks/encryption/:track_id/keys', checkAuth, limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const track = await Track.getTrack(trackId);
        if (!track) res.status(404).send({ 'error': 'not found' });
        else {
            if (!track.key || !track.keyId) res.status(404).send({ 'error': 'not found' });
            else res.status(200).json({ 'key': track.key, 'keyId': track.keyId });
        }
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// delete track
router.delete('/tracks/delete/:track_id', checkAuth, limiter, async(req, res) => {
    try {
        const userId = req.user._id;
        const trackId = req.params.track_id;
        const deleteTrack = await Track.deleteTrack(userId, trackId);
        if (!deleteTrack) res.status(404).json({ 'error': 'cannot delete track' });
        else res.status(200).json({ 'success': 'deleted track' });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// get related track
router.get('/tracks/related/:track_id', checkAuth, limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const tracksRelated = await Track.getRelatedTrack(trackId);
        if (!tracksRelated) res.status(404).json({ 'error': 'no related tracks found' });
        else res.status(200).json(tracksRelated);
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }

})


// get related track
router.get('/tracks/related/full-track/:track_id', limiter, checkAuth, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const userId = req.user._id;
        const tracksRelated = await Track.getFullRelatedTracks(userId, trackId);
        if (!tracksRelated) res.status(404).json({ 'error': 'no related tracks found' });
        else res.status(200).json(tracksRelated);
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }

})

// update track 
router.put('/tracks/update/:track_id', limiter, checkAuth, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const userId = req.user._id;
        const info = req.body;
        const updateTrack = await Track.updateTrack(userId, trackId, info);
        if (!updateTrack) res.status(400).json({ 'error': 'cannot update track' });
        else res.json(updateTrack);
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

// get track statistics 

router.get('/tracks/listeners-per-day/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const year = req.query.year;
        const day = req.query.day;
        const month = req.query.month;
        const num = await Track.getTrackListenersPerDay(trackId, year, month, day);
        res.status(200).json({ 'numberOfListners': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

router.get('/tracks/listeners-per-month/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const year = req.query.year;
        const month = req.query.month;
        const num = await Track.getTrackListenersPerMonth(trackId, year, month);
        res.status(200).json({ 'numberOfListners': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})


router.get('/tracks/listeners-per-year/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const year = req.query.year;
        const num = await Track.getTrackListenersPerYear(trackId, year);
        res.status(200).json({ 'numberOfListners': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})
router.get('/tracks/likes-per-day/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const day = req.query.day;
        const month = req.query.month;
        const year = req.query.year;
        const num = await Track.getTrackLikesPerDay(trackId, day, month, year);
        res.status(200).json({ 'numberOfLikes': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

router.get('/tracks/likes-per-month/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const month = req.query.month;
        const year = req.query.year;
        const num = await Track.getTrackLikesPerMonth(trackId, month, year);
        res.status(200).json({ 'numberOfLikes': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})

router.get('/tracks/likes-per-year/:track_id', limiter, async(req, res) => {
    try {
        const trackId = req.params.track_id;
        const year = req.query.year;
        const num = await Track.getTrackLikesPerYear(trackId, year);
        res.status(200).json({ 'numberOfLikes': num });
    } catch (ex) {
        res.status(400).send({ 'error': 'error in making the request' });
    }
})
module.exports = router;