From 19051292018317638aa390bce0550bf2fdfa6302 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 00:00:25 -0400 Subject: [PATCH 1/6] getMp3s and getMp4s now have dedicated functions downloaded files now get recorded in db.json. So when the server wants to get audio/video files, it doesn't need to recursively go through the respective folders each time - getMp4s/getMp3s API request latency is reduced ~2x (130ms -> 60ms) in testing Modified tomp3/tomp4 code to automatically add newly downloaded files to the db Added a migration so users on 3.5 or below will get their files automatically added to the db on the first run All these changes are necessary to enable easy sharing with features like timestamps --- backend/app.js | 268 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 200 insertions(+), 68 deletions(-) diff --git a/backend/app.js b/backend/app.js index 2c0a05d..bd4f625 100644 --- a/backend/app.js +++ b/backend/app.js @@ -32,6 +32,9 @@ const FileSync = require('lowdb/adapters/FileSync') const adapter = new FileSync('./appdata/db.json'); const db = low(adapter) +// check if debug mode +let debugMode = process.env.YTDL_MODE === 'debug'; + // logging setup // console format @@ -39,7 +42,7 @@ const defaultFormat = winston.format.printf(({ level, message, label, timestamp return `${timestamp} ${level.toUpperCase()}: ${message}`; }); const logger = winston.createLogger({ - level: 'info', + level: !debugMode ? 'info' : 'debug', format: winston.format.combine(winston.format.timestamp(), defaultFormat), defaultMeta: {}, transports: [ @@ -65,9 +68,14 @@ db.defaults( audio: [], video: [] }, + files: { + audio: [], + video: [] + }, configWriteFlag: false, subscriptions: [], - pin_md5: '' + pin_md5: '', + files_to_db_migration_complete: false }).write(); // config values @@ -90,9 +98,6 @@ var options = null; // encryption options var url_domain = null; var updaterStatus = null; -// check if debug mode -let debugMode = process.env.YTDL_MODE === 'debug'; - if (debugMode) logger.info('YTDL-Material in debug mode!'); // check if just updated @@ -154,6 +159,56 @@ function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, p // actual functions +async function checkMigrations() { + return new Promise(async resolve => { + // 3.5->3.6 migration + const files_to_db_migration_complete = db.get('files_to_db_migration_complete').value(); + + if (!files_to_db_migration_complete) { + logger.info('Beginning migration: 3.5->3.6+') + runFilesToDBMigration().then(success => { + if (success) { logger.info('3.5->3.6+ migration complete!'); } + else { logger.error('Migration failed: 3.5->3.6+'); } + }); + } + + resolve(true); + }); +} + +async function runFilesToDBMigration() { + return new Promise(async resolve => { + try { + let mp3s = getMp3s(); + let mp4s = getMp4s(); + + for (let i = 0; i < mp3s.length; i++) { + let file_obj = mp3s[i]; + const file_already_in_db = db.get('files.audio').find({id: file_obj.id}).value(); + if (!file_already_in_db) { + logger.verbose(`Migrating file ${file_obj.id}`); + registerFileDB(file_obj.id + '.mp3', 'audio'); + } + } + + for (let i = 0; i < mp4s.length; i++) { + let file_obj = mp4s[i]; + const file_already_in_db = db.get('files.video').find({id: file_obj.id}).value(); + if (!file_already_in_db) { + logger.verbose(`Migrating file ${file_obj.id}`); + registerFileDB(file_obj.id + '.mp4', 'video'); + } + } + + // sets migration to complete + db.set('files_to_db_migration_complete', true).write(); + resolve(true); + } catch(err) { + resolve(false); + } + }); +} + async function startServer() { if (process.env.USING_HEROKU && process.env.PORT) { // default to heroku port if using heroku @@ -435,7 +490,7 @@ async function setConfigFromEnv() { } async function loadConfig() { - return new Promise(resolve => { + return new Promise(async resolve => { url = !debugMode ? config_api.getConfigItem('ytdl_url') : 'http://localhost:4200'; backendPort = config_api.getConfigItem('ytdl_port'); usingEncryption = config_api.getConfigItem('ytdl_use_encryption'); @@ -483,6 +538,9 @@ async function loadConfig() { }, subscriptionsCheckInterval * 1000); } + // check migrations + await checkMigrations(); + // start the server here startServer(); @@ -544,6 +602,64 @@ function generateEnvVarConfigItem(key) { return {key: key, value: process['env'][key]}; } +function getMp3s() { + let mp3s = []; + var files = recFindByExt(audioFolderPath, 'mp3'); // fs.readdirSync(audioFolderPath); + for (let i = 0; i < files.length; i++) { + let file = files[i]; + var file_path = file.substring(audioFolderPath.length, file.length); + + var stats = fs.statSync(file); + + var id = file_path.substring(0, file_path.length-4); + var jsonobj = getJSONMp3(id); + if (!jsonobj) continue; + var title = jsonobj.title; + var url = jsonobj.webpage_url; + var uploader = jsonobj.uploader; + var upload_date = jsonobj.upload_date; + upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`; + + var size = stats.size; + + var thumbnail = jsonobj.thumbnail; + var duration = jsonobj.duration; + var isaudio = true; + var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date); + mp3s.push(file_obj); + } + return mp3s; +} + +function getMp4s(relative_path = true) { + let mp4s = []; + var files = recFindByExt(videoFolderPath, 'mp4'); + for (let i = 0; i < files.length; i++) { + let file = files[i]; + var file_path = file.substring(videoFolderPath.length, file.length); + + var stats = fs.statSync(file); + + var id = file_path.substring(0, file_path.length-4); + var jsonobj = getJSONMp4(id); + if (!jsonobj) continue; + var title = jsonobj.title; + var url = jsonobj.webpage_url; + var uploader = jsonobj.uploader; + var upload_date = jsonobj.upload_date; + upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`; + var thumbnail = jsonobj.thumbnail; + var duration = jsonobj.duration; + + var size = stats.size; + + var isaudio = false; + var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date); + mp4s.push(file_obj); + } + return mp4s; +} + function getThumbnailMp3(name) { var obj = getJSONMp3(name); @@ -856,6 +972,62 @@ function recFindByExt(base,ext,files,result) return result } +function registerFileDB(full_file_path, type) { + const file_id = full_file_path.substring(0, full_file_path.length-4); + const file_object = generateFileObject(file_id, type); + if (!file_object) { + logger.error(`Could not find associated JSON file for ${type} file ${file_id}`); + return false; + } + + file_object['uid'] = uuid(); + path_object = path.parse(file_object['path']); + file_object['path'] = path.format(path_object); + db.get(`files.${type}`) + .push(file_object) + .write(); +} + +function generateFileObject(id, type) { + var jsonobj = (type === 'audio') ? getJSONMp3(id) : getJSONMp4(id); + if (!jsonobj) { + return null; + } + const ext = (type === 'audio') ? '.mp3' : '.mp4' + const file_path = getTrueFileName(jsonobj['_filename'], type); // path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext); + var stats = fs.statSync(path.join(__dirname, file_path)); + + var title = jsonobj.title; + var url = jsonobj.webpage_url; + var uploader = jsonobj.uploader; + var upload_date = jsonobj.upload_date; + upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`; + + var size = stats.size; + + var thumbnail = jsonobj.thumbnail; + var duration = jsonobj.duration; + var isaudio = type === 'audio'; + var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date); + return file_obj; +} + +// replaces .webm with appropriate extension +function getTrueFileName(unfixed_path, type) { + let fixed_path = unfixed_path; + + const new_ext = (type === 'audio' ? 'mp3' : 'mp4'); + let unfixed_parts = unfixed_path.split('.'); + const old_ext = unfixed_parts[unfixed_parts.length-1]; + + + if (old_ext !== new_ext) { + unfixed_parts[unfixed_parts.length-1] = new_ext; + fixed_path = unfixed_parts.join('.'); + } + return fixed_path; +} + function getAudioInfos(fileNames) { let result = []; for (let i = 0; i < fileNames.length; i++) { @@ -1182,9 +1354,10 @@ app.post('/api/tomp3', async function(req, res) { continue; } - const filename_no_extension = removeFileExtension(output_json['_filename']); + const filepath_no_extension = removeFileExtension(output_json['_filename']); - var full_file_path = filename_no_extension + '.mp3'; + var full_file_path = filepath_no_extension + '.mp3'; + var file_name = filepath_no_extension.substring(audioFolderPath.length, filepath_no_extension.length); if (fs.existsSync(full_file_path)) { let tags = { title: output_json['title'], @@ -1193,12 +1366,14 @@ app.post('/api/tomp3', async function(req, res) { // NodeID3.create(tags, function(frame) { }) let success = NodeID3.write(tags, full_file_path); if (!success) logger.error('Failed to apply ID3 tag to audio file ' + full_file_path); + + // registers file in DB + registerFileDB(full_file_path.substring(audioFolderPath.length, full_file_path.length), 'audio'); } else { - logger.info('Output mp3 does not exist'); + logger.error('Download failed: Output mp3 does not exist'); } - var file_path = filename_no_extension.substring(audioFolderPath.length, filename_no_extension.length); - if (file_path) file_names.push(file_path); + if (file_name) file_names.push(file_name); } let is_playlist = file_names.length > 1; @@ -1318,7 +1493,13 @@ app.post('/api/tomp4', async function(req, res) { if (!output_json) { continue; } - var file_name = output_json['_filename'].replace(/^.*[\\\/]/, ''); + // var file_name = output_json['_filename'].replace(/^.*[\\\/]/, ''); + + // get filepath with no extension + const filepath_no_extension = removeFileExtension(output_json['_filename']); + + var full_file_path = filepath_no_extension + '.mp4'; + var file_name = filepath_no_extension.substring(audioFolderPath.length, filepath_no_extension.length); // renames file if necessary due to bug if (!fs.existsSync(output_json['_filename'] && fs.existsSync(output_json['_filename'] + '.webm'))) { @@ -1328,11 +1509,11 @@ app.post('/api/tomp4', async function(req, res) { } catch(e) { } } - var alternate_file_name = file_name.substring(0, file_name.length-4); - var file_path = output_json['_filename'].substring(audioFolderPath.length, output_json['_filename'].length); - // remove extension from file path - var alternate_file_path = file_path.replace(/\.[^/.]+$/, "") - if (alternate_file_name) file_names.push(alternate_file_path); + + // registers file in DB + registerFileDB(full_file_path.substring(videoFolderPath.length, full_file_path.length), 'video'); + + if (file_name) file_names.push(file_name); } let is_playlist = file_names.length > 1; @@ -1399,32 +1580,8 @@ app.post('/api/fileStatusMp4', function(req, res) { // gets all download mp3s app.post('/api/getMp3s', function(req, res) { - var mp3s = []; + var mp3s = db.get('files.audio').value(); // getMp3s(); var playlists = db.get('playlists.audio').value(); - var files = recFindByExt(audioFolderPath, 'mp3'); // fs.readdirSync(audioFolderPath); - for (let i = 0; i < files.length; i++) { - let file = files[i]; - var file_path = file.substring(audioFolderPath.length, file.length); - - var stats = fs.statSync(file); - - var id = file_path.substring(0, file_path.length-4); - var jsonobj = getJSONMp3(id); - if (!jsonobj) continue; - var title = jsonobj.title; - var url = jsonobj.webpage_url; - var uploader = jsonobj.uploader; - var upload_date = jsonobj.upload_date; - upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`; - - var size = stats.size; - - var thumbnail = jsonobj.thumbnail; - var duration = jsonobj.duration; - var isaudio = true; - var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date); - mp3s.push(file_obj); - } res.send({ mp3s: mp3s, @@ -1435,33 +1592,8 @@ app.post('/api/getMp3s', function(req, res) { // gets all download mp4s app.post('/api/getMp4s', function(req, res) { - var mp4s = []; + var mp4s = db.get('files.video').value(); // getMp4s(); var playlists = db.get('playlists.video').value(); - var fullpath = videoFolderPath; - var files = recFindByExt(videoFolderPath, 'mp4'); - for (let i = 0; i < files.length; i++) { - let file = files[i]; - var file_path = file.substring(videoFolderPath.length, file.length); - - var stats = fs.statSync(file); - - var id = file_path.substring(0, file_path.length-4); - var jsonobj = getJSONMp4(id); - if (!jsonobj) continue; - var title = jsonobj.title; - var url = jsonobj.webpage_url; - var uploader = jsonobj.uploader; - var upload_date = jsonobj.upload_date; - upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`; - var thumbnail = jsonobj.thumbnail; - var duration = jsonobj.duration; - - var size = stats.size; - - var isaudio = false; - var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date); - mp4s.push(file_obj); - } res.send({ mp4s: mp4s, From 69cd22d99230bedefcaa842762ce1c46c69e9a0f Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 01:38:35 -0400 Subject: [PATCH 2/6] file deletions now remove the file from the db as well --- backend/app.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/backend/app.js b/backend/app.js index bd4f625..d6ec687 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1493,7 +1493,6 @@ app.post('/api/tomp4', async function(req, res) { if (!output_json) { continue; } - // var file_name = output_json['_filename'].replace(/^.*[\\\/]/, ''); // get filepath with no extension const filepath_no_extension = removeFileExtension(output_json['_filename']); @@ -1814,40 +1813,50 @@ app.post('/api/deletePlaylist', async (req, res) => { // deletes mp3 file app.post('/api/deleteMp3', async (req, res) => { - var name = req.body.name; + // var name = req.body.name; + var uid = req.body.uid; + var audio_obj = db.get('files.audio').find({uid: uid}).value(); + var name = audio_obj.id; var blacklistMode = req.body.blacklistMode; var fullpath = audioFolderPath + name + ".mp3"; var wasDeleted = false; if (fs.existsSync(fullpath)) { deleteAudioFile(name, blacklistMode); + db.get('files.audio').remove({uid: uid}).write(); wasDeleted = true; res.send(wasDeleted); res.end("yes"); - } - else - { + } else if (audio_obj) { + db.get('files.audio').remove({uid: uid}).write(); + wasDeleted = true; + res.send(wasDeleted); + } else { wasDeleted = false; res.send(wasDeleted); - res.end("yes"); } }); // deletes mp4 file app.post('/api/deleteMp4', async (req, res) => { - var name = req.body.name; + var uid = req.body.uid; + var video_obj = db.get('files.video').find({uid: uid}).value(); + var name = video_obj.id; var blacklistMode = req.body.blacklistMode; var fullpath = videoFolderPath + name + ".mp4"; var wasDeleted = false; if (fs.existsSync(fullpath)) { wasDeleted = await deleteVideoFile(name, null, blacklistMode); + db.get('files.video').remove({uid: uid}).write(); // wasDeleted = true; res.send(wasDeleted); res.end("yes"); - } - else - { + } else if (video_obj) { + db.get('files.video').remove({uid: uid}).write(); + wasDeleted = true; + res.send(wasDeleted); + } else { wasDeleted = false; res.send(wasDeleted); res.end("yes"); From 2414e1602142518165166373faa35a0f99520d88 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 01:52:22 -0400 Subject: [PATCH 3/6] videos now deleted by UID ui-side --- src/app/file-card/file-card.component.ts | 3 ++- src/app/main/main.component.html | 4 ++-- src/app/posts.services.ts | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/file-card/file-card.component.ts b/src/app/file-card/file-card.component.ts index 16f33ed..7c3548b 100644 --- a/src/app/file-card/file-card.component.ts +++ b/src/app/file-card/file-card.component.ts @@ -18,6 +18,7 @@ export class FileCardComponent implements OnInit { @Input() title: string; @Input() length: string; @Input() name: string; + @Input() uid: string; @Input() thumbnailURL: string; @Input() isAudio = true; @Output() removeFile: EventEmitter = new EventEmitter(); @@ -47,7 +48,7 @@ export class FileCardComponent implements OnInit { deleteFile(blacklistMode = false) { if (!this.isPlaylist) { - this.postsService.deleteFile(this.name, this.isAudio, blacklistMode).subscribe(result => { + this.postsService.deleteFile(this.uid, this.isAudio, blacklistMode).subscribe(result => { if (result === true) { this.openSnackBar('Delete success!', 'OK.'); this.removeFile.emit(this.name); diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index d7d981f..84fe86e 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -204,7 +204,7 @@
- @@ -245,7 +245,7 @@
- diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 752cc9b..66afcfd 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -102,11 +102,11 @@ export class PostsService { return this.http.post(this.path + 'setConfig', {new_config_file: config}); } - deleteFile(name: string, isAudio: boolean, blacklistMode = false) { + deleteFile(uid: string, isAudio: boolean, blacklistMode = false) { if (isAudio) { - return this.http.post(this.path + 'deleteMp3', {name: name, blacklistMode: blacklistMode}); + return this.http.post(this.path + 'deleteMp3', {uid: uid, blacklistMode: blacklistMode}); } else { - return this.http.post(this.path + 'deleteMp4', {name: name, blacklistMode: blacklistMode}); + return this.http.post(this.path + 'deleteMp4', {uid: uid, blacklistMode: blacklistMode}); } } From 822aec4de8bd2a6bf9d0300a2a871ebc7449d124 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 14:49:05 -0400 Subject: [PATCH 4/6] added API endpoint to get file from database video/audio files can now be retrieved by just uid, allowing for easy sharing added API endpoints for sharing/unsharing a video (no UI support yet) --- backend/app.js | 108 ++++++++++++++++++- src/app/file-card/file-card.component.html | 2 +- src/app/main/main.component.ts | 20 ++-- src/app/player/player.component.html | 2 +- src/app/player/player.component.ts | 117 +++++++++++++-------- src/app/posts.services.ts | 12 +++ 6 files changed, 201 insertions(+), 60 deletions(-) diff --git a/backend/app.js b/backend/app.js index d6ec687..16fd923 100644 --- a/backend/app.js +++ b/backend/app.js @@ -986,6 +986,7 @@ function registerFileDB(full_file_path, type) { db.get(`files.${type}`) .push(file_object) .write(); + return file_object['uid']; } function generateFileObject(id, type) { @@ -1328,6 +1329,7 @@ app.post('/api/tomp3', async function(req, res) { } youtubedl.exec(url, downloadConfig, {}, function(err, output) { + var uid = null; let new_date = Date.now(); let difference = (new_date - date)/1000; logger.debug(`Audio download delay: ${difference} seconds.`); @@ -1368,7 +1370,7 @@ app.post('/api/tomp3', async function(req, res) { if (!success) logger.error('Failed to apply ID3 tag to audio file ' + full_file_path); // registers file in DB - registerFileDB(full_file_path.substring(audioFolderPath.length, full_file_path.length), 'audio'); + uid = registerFileDB(full_file_path.substring(audioFolderPath.length, full_file_path.length), 'audio'); } else { logger.error('Download failed: Output mp3 does not exist'); } @@ -1389,7 +1391,8 @@ app.post('/api/tomp3', async function(req, res) { var audiopathEncoded = encodeURIComponent(file_names[0]); res.send({ audiopathEncoded: audiopathEncoded, - file_names: is_playlist ? file_names : null + file_names: is_playlist ? file_names : null, + uid: uid }); } }); @@ -1468,6 +1471,7 @@ app.post('/api/tomp4', async function(req, res) { } youtubedl.exec(url, downloadConfig, {}, function(err, output) { + var uid = null; let new_date = Date.now(); let difference = (new_date - date)/1000; logger.debug(`Video download delay: ${difference} seconds.`); @@ -1510,7 +1514,7 @@ app.post('/api/tomp4', async function(req, res) { } // registers file in DB - registerFileDB(full_file_path.substring(videoFolderPath.length, full_file_path.length), 'video'); + uid = registerFileDB(full_file_path.substring(videoFolderPath.length, full_file_path.length), 'video'); if (file_name) file_names.push(file_name); } @@ -1528,7 +1532,8 @@ app.post('/api/tomp4', async function(req, res) { var videopathEncoded = encodeURIComponent(file_names[0]); res.send({ videopathEncoded: videopathEncoded, - file_names: is_playlist ? file_names : null + file_names: is_playlist ? file_names : null, + uid: uid }); res.end("yes"); } @@ -1601,6 +1606,101 @@ app.post('/api/getMp4s', function(req, res) { res.end("yes"); }); +app.post('/api/getFile', function (req, res) { + var uid = req.body.uid; + var type = req.body.type; + + var file = null; + + if (!type) { + file = db.get('files.audio').find({uid: uid}).value(); + if (!file) { + file = db.get('files.video').find({uid: uid}).value(); + if (file) type = 'video'; + } else { + type = 'audio'; + } + } + + if (!file && type) db.get(`files.${type}`).find({uid: uid}).value(); + + if (file) { + res.send({ + success: true, + file: file + }); + } else { + res.send({ + success: false + }); + } +}); + +// video sharing +app.post('/api/enableSharing', function(req, res) { + var type = req.body.type; + var uid = req.body.uid; + try { + success = true; + if (type === 'audio' || type === 'video') { + db.get(`files.${type}`) + .find({uid: uid}) + .assign({sharingEnabled: true}) + .write(); + } else if (type === 'playlist') { + db.get(`playlists.${type}`) + .find({id: uid}) + .assign({sharingEnabled: true}) + .write(); + } else if (type === 'subscription') { + // TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every + // time they are requested from the subscription directory. + } else { + // error + success = false; + } + + } catch(err) { + success = false; + } + + res.send({ + success: success + }); +}); + +app.post('/api/disableSharing', function(req, res) { + var type = req.body.type; + var uid = req.body.uid; + try { + success = true; + if (type === 'audio' || type === 'video') { + db.get(`files.${type}`) + .find({uid: uid}) + .assign({sharingEnabled: false}) + .write(); + } else if (type === 'playlist') { + db.get(`playlists.${type}`) + .find({id: uid}) + .assign({sharingEnabled: false}) + .write(); + } else if (type === 'subscription') { + // TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every + // time they are requested from the subscription directory. + } else { + // error + success = false; + } + + } catch(err) { + success = false; + } + + res.send({ + success: success + }); +}); + app.post('/api/subscribe', async (req, res) => { let name = req.body.name; let url = req.body.url; diff --git a/src/app/file-card/file-card.component.html b/src/app/file-card/file-card.component.html index 92116dc..f9f62a3 100644 --- a/src/app/file-card/file-card.component.html +++ b/src/app/file-card/file-card.component.html @@ -2,7 +2,7 @@
ID: {{name}}
Count: {{count}}
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 3d0a66f..568b1ce 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -390,11 +390,11 @@ export class MainComponent implements OnInit { } } - public goToFile(name, isAudio) { + public goToFile(name, isAudio, uid) { if (isAudio) { - this.downloadHelperMp3(name, false, false); + this.downloadHelperMp3(name, uid, false, false); } else { - this.downloadHelperMp4(name, false, false); + this.downloadHelperMp4(name, uid, false, false); } } @@ -407,7 +407,7 @@ export class MainComponent implements OnInit { } else { localStorage.setItem('player_navigator', this.router.url); const fileNames = playlist.fileNames; - this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID}]); + this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID, uid: playlistID}]); } } else { // playlist not found @@ -463,7 +463,7 @@ export class MainComponent implements OnInit { // download helpers - downloadHelperMp3(name, is_playlist = false, forceView = false, new_download = null) { + downloadHelperMp3(name, uid, is_playlist = false, forceView = false, new_download = null) { this.downloadingfile = false; if (this.multiDownloadMode && !this.downloadOnlyMode) { @@ -482,7 +482,7 @@ export class MainComponent implements OnInit { if (is_playlist) { this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'audio'}]); } else { - this.router.navigate(['/player', {fileNames: name, type: 'audio'}]); + this.router.navigate(['/player', {fileNames: name, type: 'audio', uid: uid}]); } } } @@ -501,7 +501,7 @@ export class MainComponent implements OnInit { } } - downloadHelperMp4(name, is_playlist = false, forceView = false, new_download = null) { + downloadHelperMp4(name, uid, is_playlist = false, forceView = false, new_download = null) { this.downloadingfile = false; if (this.multiDownloadMode && !this.downloadOnlyMode) { // do nothing @@ -519,7 +519,7 @@ export class MainComponent implements OnInit { if (is_playlist) { this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'video'}]); } else { - this.router.navigate(['/player', {fileNames: name, type: 'video'}]); + this.router.navigate(['/player', {fileNames: name, type: 'video', uid: uid}]); } } } @@ -592,7 +592,7 @@ export class MainComponent implements OnInit { this.path = is_playlist ? posts['file_names'] : posts['audiopathEncoded']; if (this.path !== '-1') { - this.downloadHelperMp3(this.path, is_playlist, false, new_download); + this.downloadHelperMp3(this.path, posts['uid'], is_playlist, false, new_download); } }, error => { // can't access server or failed to download for other reasons this.downloadingfile = false; @@ -631,7 +631,7 @@ export class MainComponent implements OnInit { this.path = is_playlist ? posts['file_names'] : posts['videopathEncoded']; if (this.path !== '-1') { - this.downloadHelperMp4(this.path, is_playlist, false, new_download); + this.downloadHelperMp4(this.path, posts['uid'], is_playlist, false, new_download); } }, error => { // can't access server this.downloadingfile = false; diff --git a/src/app/player/player.component.html b/src/app/player/player.component.html index 10b9f56..30aac2b 100644 --- a/src/app/player/player.component.html +++ b/src/app/player/player.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index 61f2fe9..d89ee12 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -25,6 +25,8 @@ export class PlayerComponent implements OnInit { original_playlist: string = null; playlist_updating = false; + show_player = false; + currentIndex = 0; currentItem: IMedia = null; api: VgAPI; @@ -33,6 +35,7 @@ export class PlayerComponent implements OnInit { fileNames: string[]; type: string; id = null; // used for playlists (not subscription) + uid = null; // used for non-subscription files (audio, video, playlist) subscriptionName = null; subPlaylist = null; @@ -53,9 +56,9 @@ export class PlayerComponent implements OnInit { ngOnInit(): void { this.innerWidth = window.innerWidth; - this.fileNames = this.route.snapshot.paramMap.get('fileNames').split('|nvr|'); this.type = this.route.snapshot.paramMap.get('type'); this.id = this.route.snapshot.paramMap.get('id'); + this.uid = this.route.snapshot.paramMap.get('uid'); this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName'); this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist'); @@ -66,53 +69,16 @@ export class PlayerComponent implements OnInit { this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio']; this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video']; this.subscriptionFolderPath = result['YoutubeDLMaterial']['Subscriptions']['subscriptions_base_path']; + this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null; - - let fileType = null; - if (this.type === 'audio') { - fileType = 'audio/mp3'; - } else if (this.type === 'video') { - fileType = 'video/mp4'; - } else if (this.type === 'subscription') { - // only supports mp4 for now - fileType = 'video/mp4'; - } else { - // error - console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.'); + if (this.uid) { + this.getFile(); } - for (let i = 0; i < this.fileNames.length; i++) { - const fileName = this.fileNames[i]; - let baseLocation = null; - let fullLocation = null; - if (!this.subscriptionName) { - baseLocation = this.type + '/'; - fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName); - } else { - // default to video but include subscription name param - baseLocation = 'video/'; - fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName + - '&subPlaylist=' + this.subPlaylist; - } - // if it has a slash (meaning it's in a directory), only get the file name for the label - let label = null; - const decodedName = decodeURIComponent(fileName); - const hasSlash = decodedName.includes('/') || decodedName.includes('\\'); - if (hasSlash) { - label = decodedName.replace(/^.*[\\\/]/, ''); - } else { - label = decodedName; - } - const mediaObject: IMedia = { - title: fileName, - src: fullLocation, - type: fileType, - label: label - } - this.playlist.push(mediaObject); + if (this.type === 'subscription' || this.fileNames) { + this.show_player = true; + this.parseFileNames(); } - this.currentItem = this.playlist[this.currentIndex]; - this.original_playlist = JSON.stringify(this.playlist); }); // this.getFileInfos(); @@ -124,6 +90,69 @@ export class PlayerComponent implements OnInit { } + getFile() { + this.postsService.getFile(this.uid, null).subscribe(res => { + if (!this.fileNames) { + // means it's a shared video + if (!this.id) { + // regular video/audio file (not playlist) + this.fileNames = [res['file']['id']]; + this.type = res['file']['isAudio'] ? 'audio' : 'video'; + this.parseFileNames(); + } + } + this.show_player = true; + }); + } + + parseFileNames() { + let fileType = null; + if (this.type === 'audio') { + fileType = 'audio/mp3'; + } else if (this.type === 'video') { + fileType = 'video/mp4'; + } else if (this.type === 'subscription') { + // only supports mp4 for now + fileType = 'video/mp4'; + } else { + // error + console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.'); + } + + for (let i = 0; i < this.fileNames.length; i++) { + const fileName = this.fileNames[i]; + let baseLocation = null; + let fullLocation = null; + if (!this.subscriptionName) { + baseLocation = this.type + '/'; + fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName); + } else { + // default to video but include subscription name param + baseLocation = 'video/'; + fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName + + '&subPlaylist=' + this.subPlaylist; + } + // if it has a slash (meaning it's in a directory), only get the file name for the label + let label = null; + const decodedName = decodeURIComponent(fileName); + const hasSlash = decodedName.includes('/') || decodedName.includes('\\'); + if (hasSlash) { + label = decodedName.replace(/^.*[\\\/]/, ''); + } else { + label = decodedName; + } + const mediaObject: IMedia = { + title: fileName, + src: fullLocation, + type: fileType, + label: label + } + this.playlist.push(mediaObject); + } + this.currentItem = this.playlist[this.currentIndex]; + this.original_playlist = JSON.stringify(this.playlist); + } + onPlayerReady(api: VgAPI) { this.api = api; diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 66afcfd..e73e311 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -118,6 +118,10 @@ export class PostsService { return this.http.post(this.path + 'getMp4s', {}); } + getFile(uid, type) { + return this.http.post(this.path + 'getFile', {uid: uid, type: type}); + } + downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null) { return this.http.post(this.path + 'downloadFile', {fileNames: fileName, type: type, @@ -147,6 +151,14 @@ export class PostsService { return this.http.post(this.path + 'checkPin', {input_pin: unhashed_pin}); } + enableSharing(uid, type) { + return this.http.post(this.path + 'enableSharing', {uid: uid, type: type}); + } + + disableSharing(uid, type) { + return this.http.post(this.path + 'disableSharing', {uid: uid, type: type}); + } + createPlaylist(playlistName, fileNames, type, thumbnailURL) { return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName, fileNames: fileNames, From 14bf2248cfbc9fdf7ed74f467ad3c3f4ca03969d Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 18:19:12 -0400 Subject: [PATCH 5/6] Added UI support for sharing videos --- backend/app.js | 40 +++++++++-- src/app/app.module.ts | 10 ++- .../share-media-dialog.component.html | 25 +++++++ .../share-media-dialog.component.scss | 0 .../share-media-dialog.component.spec.ts | 25 +++++++ .../share-media-dialog.component.ts | 71 +++++++++++++++++++ src/app/player/player.component.css | 10 ++- src/app/player/player.component.html | 2 + src/app/player/player.component.ts | 58 +++++++++++++-- src/app/posts.services.ts | 13 ++-- 10 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.html create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.scss create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts create mode 100644 src/app/dialogs/share-media-dialog/share-media-dialog.component.ts diff --git a/backend/app.js b/backend/app.js index 16fd923..69c489a 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1640,14 +1640,15 @@ app.post('/api/getFile', function (req, res) { app.post('/api/enableSharing', function(req, res) { var type = req.body.type; var uid = req.body.uid; + var is_playlist = req.body.is_playlist; try { success = true; - if (type === 'audio' || type === 'video') { + if (!is_playlist && type !== 'subscription') { db.get(`files.${type}`) .find({uid: uid}) .assign({sharingEnabled: true}) .write(); - } else if (type === 'playlist') { + } else if (is_playlist) { db.get(`playlists.${type}`) .find({id: uid}) .assign({sharingEnabled: true}) @@ -1672,15 +1673,16 @@ app.post('/api/enableSharing', function(req, res) { app.post('/api/disableSharing', function(req, res) { var type = req.body.type; var uid = req.body.uid; + var is_playlist = req.body.is_playlist; try { success = true; - if (type === 'audio' || type === 'video') { + if (!is_playlist && type !== 'subscription') { db.get(`files.${type}`) .find({uid: uid}) .assign({sharingEnabled: false}) .write(); - } else if (type === 'playlist') { - db.get(`playlists.${type}`) + } else if (is_playlist) { + db.get(`playlists.${type}`) .find({id: uid}) .assign({sharingEnabled: false}) .write(); @@ -1851,7 +1853,8 @@ app.post('/api/createPlaylist', async (req, res) => { 'name': playlistName, fileNames: fileNames, id: shortid.generate(), - thumbnailURL: thumbnailURL + thumbnailURL: thumbnailURL, + type: type }; db.get(`playlists.${type}`) @@ -1864,6 +1867,31 @@ app.post('/api/createPlaylist', async (req, res) => { }) }); +app.post('/api/getPlaylist', async (req, res) => { + let playlistID = req.body.playlistID; + let type = req.body.type; + + let playlist = null; + + if (!type) { + playlist = db.get('playlists.audio').find({id: playlistID}).value(); + if (!playlist) { + playlist = db.get('playlists.video').find({id: playlistID}).value(); + if (playlist) type = 'video'; + } else { + type = 'audio'; + } + } + + if (!playlist) playlist = db.get(`playlists.${type}`).find({id: playlistID}).value(); + + res.send({ + playlist: playlist, + type: type, + success: !!playlist + }); +}); + app.post('/api/updatePlaylist', async (req, res) => { let playlistID = req.body.playlistID; let fileNames = req.body.fileNames; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3784230..6f03650 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -25,8 +25,9 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTabsModule } from '@angular/material/tabs'; - import {DragDropModule} from '@angular/cdk/drag-drop'; - import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {DragDropModule} from '@angular/cdk/drag-drop'; +import {ClipboardModule} from '@angular/cdk/clipboard'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { AppComponent } from './app.component'; import { HttpClientModule, HttpClient } from '@angular/common/http'; import { PostsService } from 'app/posts.services'; @@ -55,6 +56,7 @@ import { VideoInfoDialogComponent } from './dialogs/video-info-dialog/video-info import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { UpdaterComponent } from './updater/updater.component'; import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component'; +import { ShareMediaDialogComponent } from './dialogs/share-media-dialog/share-media-dialog.component'; registerLocaleData(es, 'es'); export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps) { @@ -82,7 +84,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible ArgModifierDialogComponent, HighlightPipe, UpdaterComponent, - UpdateProgressDialogComponent + UpdateProgressDialogComponent, + ShareMediaDialogComponent ], imports: [ CommonModule, @@ -117,6 +120,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible MatTabsModule, MatTooltipModule, DragDropModule, + ClipboardModule, VgCoreModule, VgControlsModule, VgOverlayPlayModule, diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.html b/src/app/dialogs/share-media-dialog/share-media-dialog.component.html new file mode 100644 index 0000000..02dff40 --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.html @@ -0,0 +1,25 @@ +

+ Share playlist + Share video + Share audio +

+ + +
+
+ Enable sharing +
+
+ + + +
+
+ +
+
+
+ + + + diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.scss b/src/app/dialogs/share-media-dialog/share-media-dialog.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts b/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts new file mode 100644 index 0000000..fddbc1a --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShareMediaDialogComponent } from './share-media-dialog.component'; + +describe('ShareMediaDialogComponent', () => { + let component: ShareMediaDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ShareMediaDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ShareMediaDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts new file mode 100644 index 0000000..950a6cc --- /dev/null +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts @@ -0,0 +1,71 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { PostsService } from 'app/posts.services'; + +@Component({ + selector: 'app-share-media-dialog', + templateUrl: './share-media-dialog.component.html', + styleUrls: ['./share-media-dialog.component.scss'] +}) +export class ShareMediaDialogComponent implements OnInit { + + type = null; + uid = null; + share_url = null; + sharing_enabled = null; + is_playlist = null; + + constructor(@Inject(MAT_DIALOG_DATA) public data: any, public router: Router, private snackBar: MatSnackBar, + private postsService: PostsService) { } + + ngOnInit(): void { + if (this.data) { + this.type = this.data.type; + this.uid = this.data.uid; + this.sharing_enabled = this.data.sharing_enabled; + this.is_playlist = this.data.is_playlist; + + const arg = (this.is_playlist ? ';id=' : ';uid='); + this.share_url = window.location.href.split(';')[0] + arg + this.uid; + } + } + + copiedToClipboard() { + this.openSnackBar('Copied to clipboard!'); + } + + sharingChanged(event) { + if (event.checked) { + this.postsService.enableSharing(this.uid, this.type, this.is_playlist).subscribe(res => { + if (res['success']) { + this.openSnackBar('Sharing enabled.'); + this.sharing_enabled = true; + } else { + this.openSnackBar('Failed to enable sharing.'); + } + }, err => { + this.openSnackBar('Failed to enable sharing - server error.'); + }); + } else { + this.postsService.disableSharing(this.uid, this.type, this.is_playlist).subscribe(res => { + if (res['success']) { + this.openSnackBar('Sharing disabled.'); + this.sharing_enabled = false; + } else { + this.openSnackBar('Failed to disable sharing.'); + } + }, err => { + this.openSnackBar('Failed to disable sharing - server error.'); + }); + } + } + + public openSnackBar(message: string, action: string = '') { + this.snackBar.open(message, action, { + duration: 2000, + }); + } + +} diff --git a/src/app/player/player.component.css b/src/app/player/player.component.css index 68ccbb3..bc27f1f 100644 --- a/src/app/player/player.component.css +++ b/src/app/player/player.component.css @@ -45,13 +45,19 @@ .save-button { right: 25px; - position: absolute; + position: fixed; + bottom: 25px; +} + +.share-button { + left: 25px; + position: fixed; bottom: 25px; } .favorite-button { left: 25px; - position: absolute; + position: fixed; bottom: 25px; } diff --git a/src/app/player/player.component.html b/src/app/player/player.component.html index 30aac2b..f9221b3 100644 --- a/src/app/player/player.component.html +++ b/src/app/player/player.component.html @@ -26,8 +26,10 @@
+
+
\ No newline at end of file diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index d89ee12..a43bfad 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -6,6 +6,7 @@ import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { ShareMediaDialogComponent } from 'app/dialogs/share-media-dialog/share-media-dialog.component'; export interface IMedia { title: string; @@ -39,6 +40,11 @@ export class PlayerComponent implements OnInit { subscriptionName = null; subPlaylist = null; + is_shared = false; + + db_playlist = null; + db_file = null; + baseStreamPath = null; audioFolderPath = null; videoFolderPath = null; @@ -71,8 +77,14 @@ export class PlayerComponent implements OnInit { this.subscriptionFolderPath = result['YoutubeDLMaterial']['Subscriptions']['subscriptions_base_path']; this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null; - if (this.uid) { + if (!this.fileNames) { + this.is_shared = true; + } + + if (this.uid && !this.id) { this.getFile(); + } else if (this.id) { + this.getPlaylistFiles(); } if (this.type === 'subscription' || this.fileNames) { @@ -91,17 +103,33 @@ export class PlayerComponent implements OnInit { } getFile() { + const already_has_filenames = !!this.fileNames; this.postsService.getFile(this.uid, null).subscribe(res => { + this.db_file = res['file']; if (!this.fileNames) { // means it's a shared video if (!this.id) { // regular video/audio file (not playlist) - this.fileNames = [res['file']['id']]; - this.type = res['file']['isAudio'] ? 'audio' : 'video'; - this.parseFileNames(); + this.fileNames = [this.db_file['id']]; + this.type = this.db_file['isAudio'] ? 'audio' : 'video'; + if (!already_has_filenames) { this.parseFileNames(); } } } + if (this.db_file['sharingEnabled']) { + this.show_player = true; + } else if (!already_has_filenames) { + this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss'); + } + }); + } + + getPlaylistFiles() { + this.postsService.getPlaylist(this.id, null).subscribe(res => { + this.db_playlist = res['playlist']; + this.fileNames = this.db_playlist['fileNames']; + this.type = res['type']; this.show_player = true; + this.parseFileNames(); }); } @@ -118,7 +146,7 @@ export class PlayerComponent implements OnInit { // error console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.'); } - + this.playlist = []; for (let i = 0; i < this.fileNames.length; i++) { const fileName = this.fileNames[i]; let baseLocation = null; @@ -302,6 +330,26 @@ export class PlayerComponent implements OnInit { }) } + openShareDialog() { + const dialogRef = this.dialog.open(ShareMediaDialogComponent, { + data: { + uid: this.id ? this.id : this.uid, + type: this.type, + sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled, + is_playlist: !!this.id + }, + width: '60vw' + }); + + dialogRef.afterClosed().subscribe(res => { + if (!this.id) { + this.getFile(); + } else { + this.getPlaylistFiles(); + } + }); + } + // snackbar helper public openSnackBar(message: string, action: string) { this.snackBar.open(message, action, { diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index e73e311..e17b508 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -151,12 +151,12 @@ export class PostsService { return this.http.post(this.path + 'checkPin', {input_pin: unhashed_pin}); } - enableSharing(uid, type) { - return this.http.post(this.path + 'enableSharing', {uid: uid, type: type}); + enableSharing(uid, type, is_playlist) { + return this.http.post(this.path + 'enableSharing', {uid: uid, type: type, is_playlist: is_playlist}); } - disableSharing(uid, type) { - return this.http.post(this.path + 'disableSharing', {uid: uid, type: type}); + disableSharing(uid, type, is_playlist) { + return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist}); } createPlaylist(playlistName, fileNames, type, thumbnailURL) { @@ -166,6 +166,11 @@ export class PostsService { thumbnailURL: thumbnailURL}); } + getPlaylist(playlistID, type) { + return this.http.post(this.path + 'getPlaylist', {playlistID: playlistID, + type: type}); + } + updatePlaylist(playlistID, fileNames, type) { return this.http.post(this.path + 'updatePlaylist', {playlistID: playlistID, fileNames: fileNames, From 0ea8a11c85f9f2b29d5426c206195678c8835b23 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 7 Apr 2020 18:26:47 -0400 Subject: [PATCH 6/6] Subscriptions now don't have a share button (it's broken anyways) --- src/app/player/player.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/player/player.component.html b/src/app/player/player.component.html index f9221b3..41ce210 100644 --- a/src/app/player/player.component.html +++ b/src/app/player/player.component.html @@ -30,6 +30,6 @@
- +
\ No newline at end of file