Merge pull request #1831 from Bnyro/playlists-import-export

Playlists import and export
This commit is contained in:
Kavin 2022-11-29 19:13:49 +00:00 committed by GitHub
commit afc833bf2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 17 deletions

View File

@ -3,7 +3,19 @@
<hr /> <hr />
<button v-t="'actions.create_playlist'" class="btn" @click="createPlaylist" /> <div class="flex justify-between mb-3">
<button v-t="'actions.create_playlist'" class="btn" @click="onCreatePlaylist" />
<div class="flex">
<button
v-if="this.playlists.length > 0"
v-t="'actions.export_to_json'"
class="btn"
@click="exportPlaylists"
/>
<input id="fileSelector" ref="fileSelector" type="file" class="display-none" @change="importPlaylists" />
<label for="fileSelector" v-t="'actions.import_from_json'" class="btn ml-2" />
</div>
</div>
<div class="video-grid"> <div class="video-grid">
<div v-for="playlist in playlists" :key="playlist.id"> <div v-for="playlist in playlists" :key="playlist.id">
@ -94,22 +106,90 @@ export default {
else this.playlists = this.playlists.filter(playlist => playlist.id !== id); else this.playlists = this.playlists.filter(playlist => playlist.id !== id);
}); });
}, },
createPlaylist() { onCreatePlaylist() {
const name = prompt(this.$t("actions.create_playlist")); const name = prompt(this.$t("actions.create_playlist"));
if (name) if (!name) return;
this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, { this.createPlaylist(name).then(json => {
method: "POST", if (json.error) alert(json.error);
body: JSON.stringify({ else this.fetchPlaylists();
name: name, });
}), },
headers: { async createPlaylist(name) {
Authorization: this.getAuthToken(), let json = await this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, {
"Content-Type": "application/json", method: "POST",
}, body: JSON.stringify({
}).then(json => { name: name,
if (json.error) alert(json.error); }),
else this.fetchPlaylists(); headers: {
}); Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
});
return json;
},
async exportPlaylists() {
if (!this.playlists) return;
let json = {
format: "Piped",
version: 1,
playlists: [],
};
let tasks = [];
for (var i = 0; i < this.playlists.length; i++) {
tasks.push(this.fetchPlaylistJson(this.playlists[i].id));
}
json.playlists = await Promise.all(tasks);
this.download(JSON.stringify(json), "playlists.json", "application/json");
},
async fetchPlaylistJson(playlistId) {
let playlist = await this.fetchJson(this.authApiUrl() + "/playlists/" + playlistId);
let playlistJson = {
name: playlist.name,
// possible other types: history, watch later, ...
type: "playlist",
// as Invidious supports public and private playlists
visibility: "private",
// list of the videos, starting with "https://youtube.com" to clarify that those are YT videos
videos: [],
};
for (var i = 0; i < playlist.relatedStreams.length; i++) {
playlistJson.videos.push("https://youtube.com" + playlist.relatedStreams[i].url);
}
return playlistJson;
},
async importPlaylists() {
const file = this.$refs.fileSelector.files[0];
let text = await file.text();
let playlists = JSON.parse(text).playlists;
if (!playlists.length) {
alert(this.$t("actions.no_valid_playlists"));
return;
}
let tasks = [];
for (var i = 0; i < playlists.length; i++) {
tasks.push(this.createPlaylistWithVideos(playlists[i]));
}
await Promise.all(tasks);
window.location.reload();
},
async createPlaylistWithVideos(playlist) {
let newPlaylist = await this.createPlaylist(playlist.name);
console.log(newPlaylist);
let videoIds = playlist.videos.map(url => url.substr(-11));
await this.addVideosToPlaylist(newPlaylist.playlistId, videoIds);
},
async addVideosToPlaylist(playlistId, videoIds) {
await this.fetchJson(this.authApiUrl() + "/user/playlists/add", null, {
method: "POST",
body: JSON.stringify({
playlistId: playlistId,
videoIds: videoIds,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
});
}, },
}, },
}; };

View File

@ -118,7 +118,8 @@
"status_page": "Status", "status_page": "Status",
"source_code": "Source code", "source_code": "Source code",
"instance_donations": "Instance donations", "instance_donations": "Instance donations",
"reply_count": "{count} replies" "reply_count": "{count} replies",
"no_valid_playlists": "The file doesn't contain valid playlists!"
}, },
"comment": { "comment": {
"pinned_by": "Pinned by {author}", "pinned_by": "Pinned by {author}",