42 changed files with 1 additions and 11792 deletions
-
2Controllers/Admin.cs
-
0Controllers/Channels.cs
-
0Controllers/Raw/Private_Channel.cs
-
0Controllers/Raw/Private_Video.cs
-
0Controllers/Videos.cs
-
0MediaDB.cs
-
0Migrations/20180304034317_initial.Designer.cs
-
0Migrations/20180304034317_initial.cs
-
0Migrations/20180305045634_Tag change.Designer.cs
-
0Migrations/20180305045634_Tag change.cs
-
0Migrations/20180305052950_Made tags required.Designer.cs
-
0Migrations/20180305052950_Made tags required.cs
-
0Migrations/20180305055427_Fixed videos tags.Designer.cs
-
0Migrations/20180305055427_Fixed videos tags.cs
-
0Migrations/MediaDBModelSnapshot.cs
-
0Models/Channel.cs
-
0Models/Video.cs
-
0Program.cs
-
0Properties/launchSettings.json
-
0Startup.cs
-
0Tasks/FetchVideos.cs
-
0YTManager.csproj
-
25YTManager.sln
-
39YTManager/ScaffoldingReadMe.txt
-
15YTManager/frontend/admin/index.html
-
20YTManager/frontend/index.html
-
10969YTManager/frontend/package-lock.json
-
31YTManager/frontend/package.json
-
10YTManager/frontend/readme.md
-
126YTManager/frontend/src/components/Channel_Add.vue
-
40YTManager/frontend/src/components/Channel_Table.vue
-
45YTManager/frontend/src/components/Search.vue
-
52YTManager/frontend/src/components/Video_Grid.vue
-
43YTManager/frontend/src/components/Video_Table.vue
-
210YTManager/frontend/src/dumbyt.ts
-
15YTManager/frontend/src/index.css
-
67YTManager/frontend/src/index.ts
-
4YTManager/frontend/src/vue-shims.d.ts
-
15YTManager/frontend/tsconfig.json
-
65YTManager/frontend/webpack.config.js
-
0appsettings.Development.json
-
0appsettings.json
@ -1,25 +0,0 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio 15 |
|||
VisualStudioVersion = 15.0.26730.12 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YTManager", "YTManager\YTManager.csproj", "{14F56CC1-67A1-477F-817C-A0A7B55AE954}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {DB293C09-A2E4-4179-ADA9-BAE2093F425A} |
|||
EndGlobalSection |
|||
EndGlobal |
@ -1,39 +0,0 @@ |
|||
|
|||
ASP.NET MVC core dependencies have been added to the project. |
|||
(These dependencies include packages required to enable scaffolding) |
|||
|
|||
However you may still need to do make changes to your project. |
|||
|
|||
1. Suggested changes to Startup class: |
|||
1.1 Add a constructor: |
|||
public IConfiguration Configuration { get; } |
|||
|
|||
public Startup(IConfiguration configuration) |
|||
{ |
|||
Configuration = configuration; |
|||
} |
|||
1.2 Add MVC services: |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
// Add framework services. |
|||
services.AddMvc(); |
|||
} |
|||
|
|||
1.3 Configure web app to use use Configuration and use MVC routing: |
|||
|
|||
public void Configure(IApplicationBuilder app, IHostingEnvironment env) |
|||
{ |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseStaticFiles(); |
|||
|
|||
app.UseMvc(routes => |
|||
{ |
|||
routes.MapRoute( |
|||
name: "default", |
|||
template: "{controller=Home}/{action=Index}/{id?}"); |
|||
}); |
|||
} |
@ -1,15 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1"/> |
|||
<title>YT Manager Admin</title> |
|||
|
|||
<!-- Compressed CSS --> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" /> |
|||
</head> |
|||
<body> |
|||
<h2>Admin Page</h2> |
|||
<div id="admin_app"></div> |
|||
<script src="./../dist/build.js"></script> |
|||
</body> |
|||
</html> |
@ -1,20 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1"/> |
|||
<title>YT Manager</title> |
|||
|
|||
<!-- Compressed CSS --> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" /> |
|||
</head> |
|||
<body> |
|||
<div class="pageheader"> |
|||
<h1>Dumb YT Manager</h1> |
|||
<p>Youtube banned my account and refuses to say why, taking all my subscribed channels with it. This is a simple scrubscribed channel manager, showing recent releases from each channel and finally proper sub-catagory functionality!</p> |
|||
</div> |
|||
|
|||
<div id="app"></div> |
|||
|
|||
<script src="./dist/build.js"></script> |
|||
</body> |
|||
</html> |
10969
YTManager/frontend/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,31 +0,0 @@ |
|||
{ |
|||
"name": "dumbytmanager", |
|||
"version": "0.1.0", |
|||
"description": "Front end for the DumbYT Manager project", |
|||
"main": "index.js", |
|||
"repository": "https://gitea.hak8or.com/Almost_There/dumbytmanager", |
|||
"author": "hak8or", |
|||
"license": "MIT", |
|||
"devDependencies": { |
|||
"@types/lodash": "^4.14.104", |
|||
"axios": "^0.18.0", |
|||
"css-loader": "^0.28.10", |
|||
"style-loader": "^0.20.2", |
|||
"sweetalert2": "^7.13.3", |
|||
"ts-loader": "^4.0.0", |
|||
"typescript": "^2.7.2", |
|||
"uglifyjs-webpack-plugin": "^1.2.2", |
|||
"vue": "^2.5.13", |
|||
"vue-loader": "^14.1.1", |
|||
"vue-template-compiler": "^2.5.13", |
|||
"webpack": "^4.0.1", |
|||
"webpack-cli": "^2.0.9", |
|||
"webpack-dev-server": "^3.1.0" |
|||
}, |
|||
"scripts": { |
|||
"start": "webpack-dev-server --mode development --open", |
|||
"dev": "webpack --mode development", |
|||
"build": "webpack --mode production", |
|||
"test": "echo \"Error: no test specified\" && exit 1" |
|||
} |
|||
} |
@ -1,10 +0,0 @@ |
|||
# Frontend |
|||
|
|||
## Description |
|||
Oh god, front end web development, specifically javascript. |
|||
|
|||
This project makes use of the following: |
|||
|
|||
- Vuejs |
|||
Seemingly resonable javascript framework that's well documented and actively developed. |
|||
|
@ -1,126 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<div class="grid-x"> |
|||
<div class="medium-11 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="input-group medium-6 cell"> |
|||
<span class="input-group-label">Title</span> |
|||
<input class="input-group-field" type="text" placeholder="Title" v-model="Title"> |
|||
</div> |
|||
<div class="input-group medium-1 cell"></div> |
|||
<div class="input-group medium-5 cell"> |
|||
<span class="input-group-label">Channel</span> |
|||
<input class="input-group-field" v-bind:class="{ error: !Valid }" type="text" |
|||
placeholder="URL" v-model="Channel_Identification_Box"> |
|||
</div> |
|||
</div> |
|||
<div class="input-group"> |
|||
<span class="input-group-label">Description</span> |
|||
<input class="input-group-field" type="text" placeholder="Description" v-model="Description"> |
|||
</div> |
|||
<div class="grid-x"> |
|||
<img class="small-4 cell" :src="Thumbnail" /> |
|||
<div class="small-7"></div> |
|||
<button type="button" style="max-height:50px" class="button input-group small-1 cell" @click="Submit">Submit</button> |
|||
</div> |
|||
</form> |
|||
</transition> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue from "vue"; |
|||
import * as Dyt from "../dumbyt"; |
|||
import Axios from "axios"; |
|||
import SA2 from "sweetalert2"; |
|||
|
|||
// 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 { |
|||
Dyt.channel_modify(this.ID, Dyt.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 = ""; |
|||
}, |
|||
|
|||
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 = resp.data.items[0].snippet.description; |
|||
this.Title = resp.data.items[0].snippet.title; |
|||
this.Thumbnail = resp.data.items[0].snippet.thumbnails.medium.url; |
|||
}) |
|||
.catch(function (error) { |
|||
console.log(error); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
</script> |
@ -1,40 +0,0 @@ |
|||
<template> |
|||
<table> |
|||
<thead> |
|||
<tr> |
|||
<th>ID</th> |
|||
<th>Title</th> |
|||
<th>Description</th> |
|||
<th>User Tags</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr v-cloak v-for="channel in Channels" :key=channel.ID class="Grid_Small_Text"> |
|||
<td>{{channel.ID}}</td> |
|||
<td>{{channel.Title}}</td> |
|||
<td>{{channel.Description}}</td> |
|||
<td>{{channel.User_Tags}}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue, {PropOptions} from "vue"; |
|||
import * as Dyt from "../dumbyt"; |
|||
|
|||
// Vue class for keeping state of the videos. |
|||
export default Vue.extend({ |
|||
props: { |
|||
Channels: { |
|||
type: Array, |
|||
} as PropOptions<Dyt.Channel[]>, |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<style> |
|||
.Grid_Small_Text { |
|||
font-size: 0.7em; |
|||
} |
|||
</style> |
@ -1,45 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<input id="searchbox0" class="searchbox" type="text" v-model="searchbox_str" |
|||
placeholder="Search String goes here, for example: >5m,c++" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue, {PropOptions} from "vue"; |
|||
import * as _ from "lodash"; |
|||
import * as Dyt from "../dumbyt"; |
|||
|
|||
// Vue class for keeping state of the videos. |
|||
export default Vue.extend({ |
|||
data() { |
|||
return { |
|||
searchbox_str: "", |
|||
} |
|||
}, |
|||
|
|||
// Manually attaching functions to watchers of data. |
|||
watch: { |
|||
// Searchbox updater with debouncing. |
|||
searchbox_str (s: string) { |
|||
var v = _.debounce((s) => { |
|||
Dyt.search_videos(s).then(videos => { |
|||
this.$emit('search_complete', videos) |
|||
}); |
|||
}, 400) |
|||
v(s); |
|||
} |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.searchbox { |
|||
font-size: 1.2em; |
|||
max-width: 40em; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
margin-top: 2em; |
|||
} |
|||
</style> |
@ -1,52 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<div class="grid-x large-up-6"> |
|||
<div v-for="video in Videos" :key="video.ID" class="cell"> |
|||
<div class="card ytcard"> |
|||
<a :href="video.URL"><img :src="video.Thumbnail"></a> |
|||
<div class="card-section description"> |
|||
<div class="descriptiontitle">{{ video.Title }}</div> |
|||
<div class="descriptionchannel">{{ video.Channel }}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div > |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue, {PropOptions} from "vue"; |
|||
import * as Dyt from "../dumbyt"; |
|||
|
|||
// Vue class for keeping state of the videos. |
|||
export default Vue.extend({ |
|||
props: { |
|||
Videos: { |
|||
type: Array, |
|||
} as PropOptions<Dyt.Video[]>, |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.ytcard { |
|||
border-bottom-left-radius: 10px; |
|||
border-bottom-right-radius: 10px; |
|||
background-color:#ddd9d4; |
|||
margin: 6px 2px 6px; |
|||
} |
|||
|
|||
.description { |
|||
padding: 7px 5px 7px; |
|||
} |
|||
|
|||
.descriptiontitle { |
|||
font-size: 14px; |
|||
padding-bottom:10px; |
|||
} |
|||
|
|||
.descriptionchannel { |
|||
font-size: 10px; |
|||
text-align: right; |
|||
} |
|||
</style> |
@ -1,43 +0,0 @@ |
|||
<template> |
|||
<table> |
|||
<thead> |
|||
<tr> |
|||
<th>ID</th> |
|||
<th>Title</th> |
|||
<th>Channel</th> |
|||
<th>Seconds</th> |
|||
<th>Tags</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr v-cloak v-for="video in Videos" :key=video.ID class="Grid_Small_Text"> |
|||
<td>{{video.ID}}</td> |
|||
<td>{{video.Title}}</td> |
|||
<td>{{video.Channel}}</td> |
|||
<td>{{video.Seconds}}</td> |
|||
<td>{{video.Tags}}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue, {PropOptions} from "vue"; |
|||
import * as Dyt from "../dumbyt"; |
|||
|
|||
// Vue class for keeping state of the videos. |
|||
export default Vue.extend({ |
|||
props: { |
|||
Videos: { |
|||
type: Array, |
|||
} as PropOptions<Dyt.Video[]>, |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<style> |
|||
.Grid_Small_Text { |
|||
font-size: 0.7em; |
|||
} |
|||
</style> |
|||
|
@ -1,210 +0,0 @@ |
|||
import Axios from "axios"; |
|||
|
|||
// Base URL for API queries.
|
|||
const API_BASE_URL = "/api"; |
|||
|
|||
// How many chars at most for the description.
|
|||
const max_description_length = 100; |
|||
|
|||
// Wrapper for channels returned from our server.
|
|||
export class Channel { |
|||
// Title of the channel according to youtube.
|
|||
public Title: string; |
|||
|
|||
// Description of channel according to youtube.
|
|||
public Description: string; |
|||
|
|||
// Youtube ID
|
|||
public ID: string; |
|||
|
|||
// Tags given to the video.
|
|||
public User_Tags: Array<string>; |
|||
|
|||
// ID's belonging to the channel.
|
|||
public Video_IDs: Array<string>; |
|||
|
|||
// Popuplate this using data from our server.
|
|||
constructor(c : any){ |
|||
if (c == null) { |
|||
this.Title = "NULL" |
|||
this.Description = "NULL" |
|||
this.ID = "NULL" |
|||
this.User_Tags = ["NULL", "NULL", "NULL"] |
|||
this.Video_IDs = ["NULL", "NULL"] |
|||
} else { |
|||
this.Title = c.title; |
|||
this.Description = c.description; |
|||
this.ID = c.id; |
|||
this.User_Tags = c.user_Tags; |
|||
this.Video_IDs = c.video_IDs; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Wrapper for videos returned from our server.
|
|||
export class Video { |
|||
// Title of the video according to youtube.
|
|||
public Title: string; |
|||
|
|||
// Description of video according to youtube.
|
|||
public Description: string; |
|||
|
|||
// Youtube ID
|
|||
public ID: string; |
|||
|
|||
// Thumbnail
|
|||
public Thumbnail: string; |
|||
|
|||
// What channel made this video.
|
|||
public Channel: string; |
|||
|
|||
// Duration of the video in seconds.
|
|||
public Seconds: number; |
|||
|
|||
// Tags relevant to the video.
|
|||
public Tags: Array<string>; |
|||
|
|||
// Youtube URL of the video.
|
|||
public URL: string; |
|||
|
|||
// Popuplate this using data from our server.
|
|||
constructor(v: any){ |
|||
if (v == null){ |
|||
this.Title = "NULL"; |
|||
this.Description = "NULL"; |
|||
this.ID = "NULL"; |
|||
this.Thumbnail = "NULL"; |
|||
this.Channel = "NULL"; |
|||
this.Seconds = 9999999; |
|||
this.Tags = ["NULL", "NULL"]; |
|||
this.URL = "NULL"; |
|||
} else { |
|||
this.Title = v.title; |
|||
this.Description = v.description; |
|||
this.ID = v.id; |
|||
this.Thumbnail = v.thumbnail; |
|||
this.Channel = v.channel; |
|||
this.Seconds = v.seconds; |
|||
this.Tags = v.tags; |
|||
this.URL = "https://www.youtube.com/watch?v=" + v.id; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Types of modifications which can be applied to various models.
|
|||
export enum Modification { |
|||
Add, Delete, Refresh |
|||
} |
|||
|
|||
// Change a channel state.
|
|||
export async function channel_modify(youtubeID : string, modify : Modification): Promise<void | string> { |
|||
switch (modify){ |
|||
case Modification.Add: { |
|||
let URL = API_BASE_URL + '/Channels/' + youtubeID; |
|||
let resp = await Axios.post(URL).catch((error) => { |
|||
if (error.response) |
|||
return error.response.data; |
|||
else if (error.request) |
|||
return error.request; |
|||
else |
|||
return "Axios request failed for unkown reason."; |
|||
}); |
|||
if (typeof resp == "string") |
|||
return resp; |
|||
break; |
|||
} |
|||
case Modification.Delete: { |
|||
let URL = API_BASE_URL + '/Channels/' + youtubeID; |
|||
let resp = await Axios.delete(URL).catch(e => console.log(e)); |
|||
if (resp != null){ |
|||
if (resp.status != 200) |
|||
return resp.data(); |
|||
else |
|||
return; |
|||
} else { |
|||
return "Response is null"; |
|||
} |
|||
} |
|||
case Modification.Refresh: { |
|||
let URL = API_BASE_URL + '/Channels/Update/' + youtubeID; |
|||
let resp = await Axios.post(URL).catch(e => console.log(e)); |
|||
if (resp != null){ |
|||
if (resp.status != 200) |
|||
return resp.data(); |
|||
else |
|||
return; |
|||
} else { |
|||
return "Response is null"; |
|||
} |
|||
} |
|||
default: { |
|||
console.log("Unknown request type, error ..."); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Delete a video.
|
|||
export async function video_delete(youtubeID : string) : Promise<void> { |
|||
let URL = API_BASE_URL + '/Videos/' + youtubeID; |
|||
let resp = await Axios.delete(URL).catch(e => console.log(e)); |
|||
if ((resp == null) || (resp.data == null)){ |
|||
console.log("Video delete via " + URL + " FAIL"); |
|||
}; |
|||
} |
|||
|
|||
// Search for channels based on a search string.
|
|||
export async function search_channels(searchstr : string): Promise<Array<Channel>> { |
|||
// Temporary holder for data.
|
|||
let Channels : Array<Channel> = []; |
|||
|
|||
// Ask server for data.
|
|||
let resp = await Axios.get(API_BASE_URL + '/Channels/' + searchstr).catch(e => console.log(e)); |
|||
|
|||
// Handle all our nulls.
|
|||
if (resp == null || resp.data == null) |
|||
console.log("server /api/Channels/" + searchstr + " return is null"); |
|||
else { |
|||
// Parse our videos.
|
|||
resp.data.forEach((c: any) => { |
|||
// Trim description if needed.
|
|||
if (c.description.length > max_description_length) { |
|||
c.description = c.description.substring(0, max_description_length) + " ..."; |
|||
} |
|||
|
|||
// Add it to our array
|
|||
Channels.push(new Channel(c)); |
|||
}); |
|||
} |
|||
|
|||
// Send back the resulting videos.
|
|||
return Promise.resolve(Channels); |
|||
} |
|||
|
|||
// Search for videos based on a search string.
|
|||
export async function search_videos(searchstr : string): Promise<Array<Video>> { |
|||
// Temporary holder for videos.
|
|||
let Videos : Array<Video> = []; |
|||
|
|||
// Ask server for videos.
|
|||
let resp = await Axios.get(API_BASE_URL + '/Videos/' + searchstr).catch(e => console.log(e)); |
|||
|
|||
// Handle all our nulls.
|
|||
if (resp == null || resp.data == null) |
|||
console.log("server /videos/ return is null"); |
|||
else { |
|||
// Parse our videos.
|
|||
resp.data.forEach((v: any) => { |
|||
// Trim description if needed.
|
|||
if (v.description.length > max_description_length) { |
|||
v.description = v.description.substring(0, max_description_length) + " ..."; |
|||
} |
|||
|
|||
// Add it to our array
|
|||
Videos.push(new Video(v)); |
|||
}); |
|||
} |
|||
|
|||
// Send back the resulting videos.
|
|||
return Promise.resolve(Videos); |
|||
} |
@ -1,15 +0,0 @@ |
|||
body { |
|||
margin-left: 5%; |
|||
margin-right: 5%; |
|||
line-height: 1.6; |
|||
font-size: 18px; |
|||
color: #003636; |
|||
background-color: #F8F8F8; |
|||
max-width: 1280px; |
|||
margin: auto; |
|||
} |
|||
|
|||
.pageheader { |
|||
max-width: 960px; |
|||
margin: auto; |
|||
} |
@ -1,67 +0,0 @@ |
|||
import Vue from "vue"; |
|||
import VGC from "./components/Video_Grid.vue"; |
|||
import SCH from "./components/Search.vue"; |
|||
import VTBL from "./components/Video_Table.vue"; |
|||
import CTBL from "./components/Channel_Table.vue"; |
|||
import CADD from "./components/Channel_Add.vue"; |
|||
import './index.css'; |
|||
import { Video, search_videos, Channel, search_channels } from "./dumbyt"; |
|||
|
|||
let MainApp = new Vue({ |
|||
el: "#app", |
|||
template: `
|
|||
<div> |
|||
<SCH v-on:search_complete="search_completed"/> |
|||
<VGC v-bind:Videos="Videos"/> |
|||
</div> |
|||
`,
|
|||
components: { |
|||
SCH, |
|||
VGC |
|||
}, |
|||
data() { |
|||
return { |
|||
Videos: Array<Video>() |
|||
} |
|||
}, |
|||
methods: { |
|||
// Callback for when the search component got results back.
|
|||
search_completed(videos : Array<Video>) : void { |
|||
this.Videos = videos; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
let AdminApp = new Vue({ |
|||
el: "#admin_app", |
|||
template: `
|
|||
<div> |
|||
<SCH v-on:search_complete="search_completed"/> |
|||
<CADD/> |
|||
<CTBL v-bind:Channels="Channels"/> |
|||
<VTBL v-bind:Videos="Videos"/> |
|||
</div> |
|||
`,
|
|||
components: { |
|||
SCH, |
|||
CADD, |
|||
CTBL, |
|||
VTBL |
|||
}, |
|||
data() { |
|||
return { |
|||
Videos: Array<Video>(), |
|||
Channels: Array<Channel>() |
|||
} |
|||
}, |
|||
methods: { |
|||
// Callback for when the search component got results back.
|
|||
search_completed(videos : Array<Video>) : void { |
|||
this.Videos = videos; |
|||
} |
|||
}, |
|||
mounted(){ |
|||
// Populate the channels field immediatly on start up.
|
|||
search_channels("").then(channels => this.Channels = channels); |
|||
} |
|||
}); |
@ -1,4 +0,0 @@ |
|||
declare module "*.vue" { |
|||
import Vue from "vue"; |
|||
export default Vue; |
|||
} |
@ -1,15 +0,0 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"outDir": "./built/", |
|||
"sourceMap": true, |
|||
"strict": true, |
|||
"noImplicitReturns": true, |
|||
"module": "commonjs", |
|||
"moduleResolution": "node", |
|||
"target": "es6", |
|||
"noImplicitAny": true |
|||
}, |
|||
"include": [ |
|||
"./src/**/*" |
|||
] |
|||
} |
@ -1,65 +0,0 @@ |
|||
var path = require('path') |
|||
var webpack = require('webpack') |
|||
|
|||
module.exports = { |
|||
entry: './src/index.ts', |
|||
output: { |
|||
path: path.resolve(__dirname, './dist'), |
|||
publicPath: '/dist/', |
|||
filename: 'build.js' |
|||
}, |
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /\.vue$/, |
|||
loader: 'vue-loader', |
|||
options: { |
|||
loaders: { |
|||
// Since sass-loader (weirdly) has SCSS as its default parse mode, we map
|
|||
// the "scss" and "sass" values for the lang attribute to the right configs here.
|
|||
// other preprocessors should work out of the box, no loader config like this necessary.
|
|||
'scss': 'vue-style-loader!css-loader!sass-loader', |
|||
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax', |
|||
} |
|||
// other vue-loader options go here
|
|||
} |
|||
}, |
|||
{ |
|||
test: /\.tsx?$/, |
|||
loader: 'ts-loader', |
|||
exclude: /node_modules/, |
|||
options: { |
|||
appendTsSuffixTo: [/\.vue$/], |
|||
} |
|||
}, |
|||
{ |
|||
test: /\.(png|jpg|gif|svg)$/, |
|||
loader: 'file-loader', |
|||
options: { |
|||
name: '[name].[ext]?[hash]' |
|||
} |
|||
}, |
|||
{ |
|||
test: /\.css$/, |
|||
use: [ |
|||
'style-loader', |
|||
'css-loader' |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
resolve: { |
|||
extensions: ['.ts', '.js', '.vue', '.json'], |
|||
alias: { |
|||
'vue$': 'vue/dist/vue.esm.js' |
|||
} |
|||
}, |
|||
devServer: { |
|||
historyApiFallback: true, |
|||
noInfo: true |
|||
}, |
|||
performance: { |
|||
hints: false |
|||
}, |
|||
devtool: '#eval-source-map' |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue