FrontEnd/src/components/Channel_Add.vue

240 lines
8.6 KiB
Vue

<template>
<div>
<div class="grid-x">
<div class="medium-7 cell"></div>
<div class="medium-1 cell">
<button type="button" class="button input-group" v-on:click='importchannel'>
<i class="fa fa-plus" aria-hidden="true"></i> Import
</button>
</div>
<div class="medium-1 cell"></div>
<div class="medium-1 cell">
<button type="button" class="button input-group" v-on:click='exportchannel'>
<i class="fa fa-plus" aria-hidden="true"></i> Export
</button>
</div>
<div class="medium-1 cell"></div>
<div class="medium-1 cell">
<button type="button" class="button input-group" v-on:click='Expanded = !Expanded'>
<i class="fa fa-plus" aria-hidden="true"></i> Add
</button>
</div>
</div>
<transition name="fade">
<form v-cloak v-if="Expanded">
<h2>Add new Channel</h2>
<div class="grid-x">
<div class="medium-7 cell">
<p class="input-group">
<span class="input-group-label">Title</span>
<input class="input-group-field" type="text" placeholder="Youtube Channel Title"
v-model="Title" readonly>
</p>
<p class="input-group">
<span class="input-group-label">Channel ID</span>
<input class="input-group-field" v-bind:class="{ error: !Valid }" type="text"
placeholder="https://www.youtube.com/channel/UCJkMlOu7faDgqh4PfzbpLdg"
v-model="Channel_Identification_Box">
</p>
<p class="input-group">
<span class="input-group-label">Description</span>
<input class="input-group-field" placeholder="Description"
v-model="Description" readonly cols="15" rows="5"
type="text">
</p>
<button type="button"
class="button input-group" style="max-height:50px; max-width:100px; float: right;"
@click="Submit">Submit</button>
</div>
<div class="medium-2 cell"></div>
<div class="input-group medium-2 cell">
<img :src="Thumbnail"/>
</div>
<div class="medium-1 cell"></div>
</div>
</form>
</transition>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import * as DumbYT from "../dumbyt";
import Axios from "axios";
import SA2,{ SweetAlertType } from "sweetalert2";
import * as _ from "lodash";
import Sweetalert2, {SweetAlertOptions} from "sweetalert2";
// Small wrapper to convert to and from channels and import/export possible channels.
class OutChannel {
ID : string;
Tags : Array<string>;
Title: string;
constructor(channel :DumbYT.Channel) {
this.ID = channel.ID;
this.Title = channel.Title;
this.Tags = channel.User_Tags;
}
};
async function Channel_Bulk_Add() : Promise<void> {
// Ask user for the JSON file containing our channels.
const {value: uploadedfile} = await SA2({
title: 'Select image',
input: 'file',
inputAttributes: {
'aria-label': 'Upload your profile picture'
}
});
// Check that we got an input file.
if (uploadedfile != null) {
// Read our file.
var reader = new FileReader();
// Tell reader to run upload our channels when it got a file.
reader.onload = async (e) => {
// Send all requested channel updates to the server.
const channels = JSON.parse(reader.result) as OutChannel[];
const attempts = await Promise.all(channels.map(c => DumbYT.API.channel_modify(c.ID, DumbYT.API.Modification.Add)));
// Get all the insertions which failed.
const bad = attempts.filter((a: any): a is string => !!a).map(a => {return {
title: "Channel Add Fail!",
text: "The channel has not been added due to the following: \n" + a,
type: "error"} as SweetAlertOptions
});
// Make dialog boxes for them.
await SA2.queue(bad);
}
// Give our uploader the data which was sent.
reader.readAsText(uploadedfile)
}
}
// Vue class for keeping state of the videos.
export default Vue.extend({
data() {
return {
Title: "",
Description: "",
ID: "",
Thumbnail: "",
Valid: false,
Expanded: false,
Channel_Identification_Box : ""
}
},
watch: {
Channel_Identification_Box(newcontents: string) {
this.GetChannelFromYT(newcontents);
}
},
methods: {
Submit() : void {
DumbYT.API.channel_modify(this.ID, DumbYT.API.Modification.Add).then((resp) => {
if (resp == null)
SA2(
"Channel Add Success!",
"The channel has been added succesfully, video contents should be updated shortly.",
"success"
);
else
SA2(
"Channel Add Fail!",
"The channel has not been added due to the following: \n" + resp,
"error"
);
});
// Hide the bars and erase internal state.
this.Expanded = false;
this.Title = "";
this.Description = "";
this.ID = "";
this.Thumbnail = "";
this.Valid = false;
this.Channel_Identification_Box = "";
},
// Export the channels as a json file.
exportchannel() : void {
function download(filename: string, text: string) : void {
var element = document.createElement('a');
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// Get all the youtube channels.
DumbYT.API.search_channels("").then(channels =>
download("Channel_Export.json",
JSON.stringify(channels.map(c => new OutChannel(c)))
)
);
},
// Import a channel list
importchannel() : void {
Channel_Bulk_Add();
},
GetChannelFromYT(Channel: string) : void {
// Say it failed first so if we exit early then correctly marked fail.
this.Valid = false;
// Copy over to internal ID box.
this.ID = Channel;
// Remove any potential youtube URL from the field.
const ytchurl = "https://www.youtube.com/channel/";
if (this.ID.startsWith(ytchurl))
this.ID = this.ID.replace(ytchurl, "");
// Check if what remains looks like a youtube channel ID.
if (this.ID.length != "UCyS4xQE6DK4_p3qXQwJQAyA".length)
return;
// Get channel metadata.
const API = 'https://www.googleapis.com/youtube/v3/channels?';
const API_Parts = 'part=snippet%2CcontentDetails%2Cstatistics';
const API_Key = '&key=AIzaSyCuIYkMc5SktlnXRXNaDf2ObX-fQvtWCnQ'
const API_Search_ID = '&id=' + this.ID;
Axios.get(API + API_Parts + API_Search_ID + API_Key).then((resp) => {
this.Description = _.truncate(resp.data.items[0].snippet.description, {length: 70});
this.Title = resp.data.items[0].snippet.title;
this.Thumbnail = resp.data.items[0].snippet.thumbnails.high.url;
})
.catch(function (error) {
console.log(error);
});
}
}
});
</script>
<style>
.fade-enter-active {
transition: opacity 0.75s;
}
.fade-leave-active {
transition: opacity 0.25s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>