mirror of
				https://github.com/TeamPiped/Piped.git
				synced 2024-08-14 23:57:27 +00:00 
			
		
		
		
	Add support for client-side watch history. (#370)
* Add support for client-side watch history. * Update watched videos more often.
This commit is contained in:
		
							parent
							
								
									6688fafeaa
								
							
						
					
					
						commit
						8feb4fbc41
					
				
					 13 changed files with 193 additions and 23 deletions
				
			
		
							
								
								
									
										17
									
								
								src/App.vue
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								src/App.vue
									
										
									
									
									
								
							| 
						 | 
					@ -41,6 +41,23 @@ export default {
 | 
				
			||||||
                default:
 | 
					                default:
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.getPreferenceBoolean("watchHistory", false))
 | 
				
			||||||
 | 
					            if ("indexedDB" in window) {
 | 
				
			||||||
 | 
					                const request = indexedDB.open("piped-db", 1);
 | 
				
			||||||
 | 
					                request.onupgradeneeded = function() {
 | 
				
			||||||
 | 
					                    const db = request.result;
 | 
				
			||||||
 | 
					                    console.log("Upgrading object store.");
 | 
				
			||||||
 | 
					                    if (!db.objectStoreNames.contains("watch_history")) {
 | 
				
			||||||
 | 
					                        const store = db.createObjectStore("watch_history", { keyPath: "videoId" });
 | 
				
			||||||
 | 
					                        store.createIndex("video_id_idx", "videoId", { unique: true });
 | 
				
			||||||
 | 
					                        store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                request.onsuccess = e => {
 | 
				
			||||||
 | 
					                    window.db = e.target.result;
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            } else console.log("This browser doesn't support IndexedDB");
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,7 @@ export default {
 | 
				
			||||||
    activated() {
 | 
					    activated() {
 | 
				
			||||||
        if (this.channel && !this.channel.error) document.title = this.channel.name + " - Piped";
 | 
					        if (this.channel && !this.channel.error) document.title = this.channel.name + " - Piped";
 | 
				
			||||||
        window.addEventListener("scroll", this.handleScroll);
 | 
					        window.addEventListener("scroll", this.handleScroll);
 | 
				
			||||||
 | 
					        if (this.channel && !this.channel.error) this.updateWatched(this.channel.relatedStreams);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    deactivated() {
 | 
					    deactivated() {
 | 
				
			||||||
        window.removeEventListener("scroll", this.handleScroll);
 | 
					        window.removeEventListener("scroll", this.handleScroll);
 | 
				
			||||||
| 
						 | 
					@ -78,6 +79,7 @@ export default {
 | 
				
			||||||
                    if (!this.channel.error) {
 | 
					                    if (!this.channel.error) {
 | 
				
			||||||
                        document.title = this.channel.name + " - Piped";
 | 
					                        document.title = this.channel.name + " - Piped";
 | 
				
			||||||
                        if (this.authenticated) this.fetchSubscribedStatus();
 | 
					                        if (this.authenticated) this.fetchSubscribedStatus();
 | 
				
			||||||
 | 
					                        this.updateWatched(this.channel.relatedStreams);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					@ -88,9 +90,9 @@ export default {
 | 
				
			||||||
                this.fetchJson(this.apiUrl() + "/nextpage/channel/" + this.channel.id, {
 | 
					                this.fetchJson(this.apiUrl() + "/nextpage/channel/" + this.channel.id, {
 | 
				
			||||||
                    nextpage: this.channel.nextpage,
 | 
					                    nextpage: this.channel.nextpage,
 | 
				
			||||||
                }).then(json => {
 | 
					                }).then(json => {
 | 
				
			||||||
                    this.channel.relatedStreams.concat(json.relatedStreams);
 | 
					 | 
				
			||||||
                    this.channel.nextpage = json.nextpage;
 | 
					                    this.channel.nextpage = json.nextpage;
 | 
				
			||||||
                    this.loading = false;
 | 
					                    this.loading = false;
 | 
				
			||||||
 | 
					                    this.updateWatched(json.relatedStreams);
 | 
				
			||||||
                    json.relatedStreams.map(stream => this.channel.relatedStreams.push(stream));
 | 
					                    json.relatedStreams.map(stream => this.channel.relatedStreams.push(stream));
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,10 +44,14 @@ export default {
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mounted() {
 | 
					    mounted() {
 | 
				
			||||||
        this.fetchFeed().then(videos => (this.videos = videos));
 | 
					        this.fetchFeed().then(videos => {
 | 
				
			||||||
 | 
					            this.videos = videos;
 | 
				
			||||||
 | 
					            this.updateWatched(this.videos);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    activated() {
 | 
					    activated() {
 | 
				
			||||||
        document.title = "Feed - Piped";
 | 
					        document.title = "Feed - Piped";
 | 
				
			||||||
 | 
					        if (this.videos.length > 0) this.updateWatched(this.videos);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        async fetchFeed() {
 | 
					        async fetchFeed() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										88
									
								
								src/components/HistoryPage.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/components/HistoryPage.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <h1 class="uk-text-bold uk-text-center">Watch History</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <br />
 | 
				
			||||||
 | 
					    Sort by:
 | 
				
			||||||
 | 
					    <select class="uk-select uk-width-auto" v-model="selectedSort" @change="onChange()">
 | 
				
			||||||
 | 
					        <option value="descending">Most Recent</option>
 | 
				
			||||||
 | 
					        <option value="ascending">Least Recent</option>
 | 
				
			||||||
 | 
					        <option value="channel_ascending">Channel Name (A-Z)</option>
 | 
				
			||||||
 | 
					        <option value="channel_descending">Channel Name (Z-A)</option>
 | 
				
			||||||
 | 
					    </select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <hr />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="uk-grid-xl" uk-grid="parallax: 0">
 | 
				
			||||||
 | 
					        <div
 | 
				
			||||||
 | 
					            :style="[{ background: backgroundColor }]"
 | 
				
			||||||
 | 
					            class="uk-width-1-2 uk-width-1-3@s uk-width-1-4@m uk-width-1-5@l uk-width-1-6@xl"
 | 
				
			||||||
 | 
					            v-bind:key="video.url"
 | 
				
			||||||
 | 
					            v-for="video in videos"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <VideoItem :video="video" />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import VideoItem from "@/components/VideoItem.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					    data() {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            videos: [],
 | 
				
			||||||
 | 
					            selectedSort: "descending",
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mounted() {
 | 
				
			||||||
 | 
					        (async () => {
 | 
				
			||||||
 | 
					            if (window.db) {
 | 
				
			||||||
 | 
					                var tx = window.db.transaction("watch_history", "readonly");
 | 
				
			||||||
 | 
					                var store = tx.objectStore("watch_history");
 | 
				
			||||||
 | 
					                const cursorRequest = store.openCursor();
 | 
				
			||||||
 | 
					                cursorRequest.onsuccess = e => {
 | 
				
			||||||
 | 
					                    const cursor = e.target.result;
 | 
				
			||||||
 | 
					                    if (cursor) {
 | 
				
			||||||
 | 
					                        const video = cursor.value;
 | 
				
			||||||
 | 
					                        this.videos.push({
 | 
				
			||||||
 | 
					                            url: "/watch?v=" + video.videoId,
 | 
				
			||||||
 | 
					                            title: video.title,
 | 
				
			||||||
 | 
					                            uploaderName: video.uploaderName,
 | 
				
			||||||
 | 
					                            uploaderUrl: video.uploaderUrl,
 | 
				
			||||||
 | 
					                            duration: video.duration,
 | 
				
			||||||
 | 
					                            thumbnail: video.thumbnail,
 | 
				
			||||||
 | 
					                            watchedAt: video.watchedAt,
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        this.videos.sort((a, b) => b.watchedAt - a.watchedAt); // TODO: Optimize
 | 
				
			||||||
 | 
					                        if (this.videos.length < 1000) cursor.continue();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    activated() {
 | 
				
			||||||
 | 
					        document.title = "Watch History - Piped";
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    methods: {
 | 
				
			||||||
 | 
					        onChange() {
 | 
				
			||||||
 | 
					            switch (this.selectedSort) {
 | 
				
			||||||
 | 
					                case "ascending":
 | 
				
			||||||
 | 
					                    this.videos.sort((a, b) => a.watchedAt - b.watchedAt);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "descending":
 | 
				
			||||||
 | 
					                    this.videos.sort((a, b) => b.watchedAt - a.watchedAt);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "channel_ascending":
 | 
				
			||||||
 | 
					                    this.videos.sort((a, b) => a.uploaderName.localeCompare(b.uploaderName));
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "channel_descending":
 | 
				
			||||||
 | 
					                    this.videos.sort((a, b) => b.uploaderName.localeCompare(a.uploaderName));
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    components: {
 | 
				
			||||||
 | 
					        VideoItem,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,6 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        login() {
 | 
					        login() {
 | 
				
			||||||
            console.log("authToken" + this.hashCode(this.apiUrl()));
 | 
					 | 
				
			||||||
            this.fetchJson(this.apiUrl() + "/login", null, {
 | 
					            this.fetchJson(this.apiUrl() + "/login", null, {
 | 
				
			||||||
                method: "POST",
 | 
					                method: "POST",
 | 
				
			||||||
                body: JSON.stringify({
 | 
					                body: JSON.stringify({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,13 @@
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
        <div class="uk-navbar-left">
 | 
					        <div class="uk-navbar-left">
 | 
				
			||||||
            <router-link class="uk-navbar-item uk-logo uk-text-bold" :style="[{ colour: foregroundColor }]" to="/"
 | 
					            <router-link class="uk-navbar-item uk-logo uk-text-bold" :style="[{ colour: foregroundColor }]" to="/"
 | 
				
			||||||
                ><img alt="logo" src="/img/icons/logo.svg" height="32" width="32" style="margin-bottom: 6px; margin-right: -13px" />iped</router-link
 | 
					                ><img
 | 
				
			||||||
 | 
					                    alt="logo"
 | 
				
			||||||
 | 
					                    src="/img/icons/logo.svg"
 | 
				
			||||||
 | 
					                    height="32"
 | 
				
			||||||
 | 
					                    width="32"
 | 
				
			||||||
 | 
					                    style="margin-bottom: 6px; margin-right: -13px"
 | 
				
			||||||
 | 
					                />iped</router-link
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="uk-navbar-center uk-flex uk-visible@m">
 | 
					        <div class="uk-navbar-center uk-flex uk-visible@m">
 | 
				
			||||||
| 
						 | 
					@ -31,6 +37,9 @@
 | 
				
			||||||
                <li v-if="shouldShowLogin">
 | 
					                <li v-if="shouldShowLogin">
 | 
				
			||||||
                    <router-link to="/register">Register</router-link>
 | 
					                    <router-link to="/register">Register</router-link>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li v-if="shouldShowHistory">
 | 
				
			||||||
 | 
					                    <router-link to="/history">History</router-link>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
                <li v-if="authenticated">
 | 
					                <li v-if="authenticated">
 | 
				
			||||||
                    <router-link to="/feed">Feed</router-link>
 | 
					                    <router-link to="/feed">Feed</router-link>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
| 
						 | 
					@ -73,6 +82,9 @@ export default {
 | 
				
			||||||
        shouldShowLogin(_this) {
 | 
					        shouldShowLogin(_this) {
 | 
				
			||||||
            return _this.getAuthToken() == null;
 | 
					            return _this.getAuthToken() == null;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        shouldShowHistory(_this) {
 | 
				
			||||||
 | 
					            return _this.getPreferenceBoolean("watchHistory", false);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        onKeyUp(e) {
 | 
					        onKeyUp(e) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="uk-flex uk-flex-between uk-flex-middle">
 | 
					    <div class="uk-flex uk-flex-between uk-flex-middle">
 | 
				
			||||||
        <router-link class="uk-button uk-button-text" to="/"
 | 
					        <button class="uk-button uk-button-text" @click="$router.go(-1) || $router.push('/')">
 | 
				
			||||||
            ><font-awesome-icon icon="chevron-left" />  Back</router-link
 | 
					            <font-awesome-icon icon="chevron-left" />  Back
 | 
				
			||||||
        >
 | 
					        </button>
 | 
				
			||||||
        <span><h1 class="uk-text-bold uk-text-center">Preferences</h1></span>
 | 
					        <span><h1 class="uk-text-bold uk-text-center">Preferences</h1></span>
 | 
				
			||||||
        <span />
 | 
					        <span />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
| 
						 | 
					@ -88,6 +88,10 @@
 | 
				
			||||||
    <b>Minimize Description by default</b>
 | 
					    <b>Minimize Description by default</b>
 | 
				
			||||||
    <br />
 | 
					    <br />
 | 
				
			||||||
    <input class="uk-checkbox" v-model="minimizeDescription" @change="onChange($event)" type="checkbox" />
 | 
					    <input class="uk-checkbox" v-model="minimizeDescription" @change="onChange($event)" type="checkbox" />
 | 
				
			||||||
 | 
					    <br />
 | 
				
			||||||
 | 
					    <b>Store Watch History</b>
 | 
				
			||||||
 | 
					    <br />
 | 
				
			||||||
 | 
					    <input class="uk-checkbox" v-model="watchHistory" @change="onChange($event)" type="checkbox" />
 | 
				
			||||||
    <h2>Instances List</h2>
 | 
					    <h2>Instances List</h2>
 | 
				
			||||||
    <table class="uk-table">
 | 
					    <table class="uk-table">
 | 
				
			||||||
        <thead>
 | 
					        <thead>
 | 
				
			||||||
| 
						 | 
					@ -147,6 +151,7 @@ export default {
 | 
				
			||||||
            defaultHomepage: "trending",
 | 
					            defaultHomepage: "trending",
 | 
				
			||||||
            showComments: true,
 | 
					            showComments: true,
 | 
				
			||||||
            minimizeDescription: false,
 | 
					            minimizeDescription: false,
 | 
				
			||||||
 | 
					            watchHistory: false,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    activated() {
 | 
					    activated() {
 | 
				
			||||||
| 
						 | 
					@ -223,6 +228,7 @@ export default {
 | 
				
			||||||
            this.defaultHomepage = this.getPreferenceString("homepage", "trending");
 | 
					            this.defaultHomepage = this.getPreferenceString("homepage", "trending");
 | 
				
			||||||
            this.showComments = this.getPreferenceBoolean("comments", true);
 | 
					            this.showComments = this.getPreferenceBoolean("comments", true);
 | 
				
			||||||
            this.minimizeDescription = this.getPreferenceBoolean("minimizeDescription", false);
 | 
					            this.minimizeDescription = this.getPreferenceBoolean("minimizeDescription", false);
 | 
				
			||||||
 | 
					            this.watchHistory = this.getPreferenceBoolean("watchHistory", false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
| 
						 | 
					@ -230,7 +236,11 @@ export default {
 | 
				
			||||||
            if (localStorage) {
 | 
					            if (localStorage) {
 | 
				
			||||||
                var shouldReload = false;
 | 
					                var shouldReload = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (this.getPreferenceString("theme", "dark") !== this.selectedTheme) shouldReload = true;
 | 
					                if (
 | 
				
			||||||
 | 
					                    this.getPreferenceString("theme", "dark") !== this.selectedTheme ||
 | 
				
			||||||
 | 
					                    this.getPreferenceBoolean("watchHistory", false) != this.watchHistory
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                    shouldReload = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                localStorage.setItem("instance", this.selectedInstance);
 | 
					                localStorage.setItem("instance", this.selectedInstance);
 | 
				
			||||||
                localStorage.setItem("sponsorblock", this.sponsorBlock);
 | 
					                localStorage.setItem("sponsorblock", this.sponsorBlock);
 | 
				
			||||||
| 
						 | 
					@ -254,6 +264,7 @@ export default {
 | 
				
			||||||
                localStorage.setItem("homepage", this.defaultHomepage);
 | 
					                localStorage.setItem("homepage", this.defaultHomepage);
 | 
				
			||||||
                localStorage.setItem("comments", this.showComments);
 | 
					                localStorage.setItem("comments", this.showComments);
 | 
				
			||||||
                localStorage.setItem("minimizeDescription", this.minimizeDescription);
 | 
					                localStorage.setItem("minimizeDescription", this.minimizeDescription);
 | 
				
			||||||
 | 
					                localStorage.setItem("watchHistory", this.watchHistory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (shouldReload) window.location.reload();
 | 
					                if (shouldReload) window.location.reload();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,6 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        register() {
 | 
					        register() {
 | 
				
			||||||
            console.log("authToken" + this.hashCode(this.apiUrl()));
 | 
					 | 
				
			||||||
            this.fetchJson(this.apiUrl() + "/register", null, {
 | 
					            this.fetchJson(this.apiUrl() + "/register", null, {
 | 
				
			||||||
                method: "POST",
 | 
					                method: "POST",
 | 
				
			||||||
                body: JSON.stringify({
 | 
					                body: JSON.stringify({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,10 +27,14 @@ export default {
 | 
				
			||||||
    mounted() {
 | 
					    mounted() {
 | 
				
			||||||
        let region = this.getPreferenceString("region", "US");
 | 
					        let region = this.getPreferenceString("region", "US");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.fetchTrending(region).then(videos => (this.videos = videos));
 | 
					        this.fetchTrending(region).then(videos => {
 | 
				
			||||||
 | 
					            this.videos = videos;
 | 
				
			||||||
 | 
					            this.updateWatched(this.videos);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    activated() {
 | 
					    activated() {
 | 
				
			||||||
        document.title = "Trending - Piped";
 | 
					        document.title = "Trending - Piped";
 | 
				
			||||||
 | 
					        if (this.videos.length > 0) this.updateWatched(this.videos);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        async fetchTrending(region) {
 | 
					        async fetchTrending(region) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,12 @@
 | 
				
			||||||
                    style="bottom: 5px; right: 5px; background: rgba(0, 0, 0, .75); color: white; padding: 0 5px;"
 | 
					                    style="bottom: 5px; right: 5px; background: rgba(0, 0, 0, .75); color: white; padding: 0 5px;"
 | 
				
			||||||
                    >{{ timeFormat(video.duration) }}</span
 | 
					                    >{{ timeFormat(video.duration) }}</span
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
 | 
					                <span
 | 
				
			||||||
 | 
					                    v-if="video.watched"
 | 
				
			||||||
 | 
					                    class="uk-label uk-border-rounded uk-position-absolute video-duration"
 | 
				
			||||||
 | 
					                    style="bottom: 5px; left: 5px; background: rgba(0, 0, 0, .75); color: white; padding: 0 5px;"
 | 
				
			||||||
 | 
					                    >Watched</span
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <p class="uk-text-break">{{ video.title }}</p>
 | 
					            <p class="uk-text-break">{{ video.title }}</p>
 | 
				
			||||||
        </router-link>
 | 
					        </router-link>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -144,6 +144,22 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mounted() {
 | 
					    mounted() {
 | 
				
			||||||
        this.getVideoData().then(() => {
 | 
					        this.getVideoData().then(() => {
 | 
				
			||||||
 | 
					            (async () => {
 | 
				
			||||||
 | 
					                if (window.db) {
 | 
				
			||||||
 | 
					                    var tx = window.db.transaction("watch_history", "readwrite");
 | 
				
			||||||
 | 
					                    var store = tx.objectStore("watch_history");
 | 
				
			||||||
 | 
					                    var video = {
 | 
				
			||||||
 | 
					                        videoId: this.getVideoId(),
 | 
				
			||||||
 | 
					                        title: this.video.title,
 | 
				
			||||||
 | 
					                        duration: this.video.duration,
 | 
				
			||||||
 | 
					                        thumbnail: this.video.thumbnailUrl,
 | 
				
			||||||
 | 
					                        uploaderUrl: this.video.uploaderUrl,
 | 
				
			||||||
 | 
					                        uploaderName: this.video.uploader,
 | 
				
			||||||
 | 
					                        watchedAt: Date.now(),
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    store.add(video);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })();
 | 
				
			||||||
            if (this.active) this.$refs.videoPlayer.loadVideo();
 | 
					            if (this.active) this.$refs.videoPlayer.loadVideo();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.getSponsors();
 | 
					        this.getSponsors();
 | 
				
			||||||
| 
						 | 
					@ -163,13 +179,6 @@ export default {
 | 
				
			||||||
        this.active = false;
 | 
					        this.active = false;
 | 
				
			||||||
        window.removeEventListener("scroll", this.handleScroll);
 | 
					        window.removeEventListener("scroll", this.handleScroll);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    watch: {
 | 
					 | 
				
			||||||
        "$route.query.v": function(v) {
 | 
					 | 
				
			||||||
            if (v) {
 | 
					 | 
				
			||||||
                window.scrollTo(0, 0);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    methods: {
 | 
					    methods: {
 | 
				
			||||||
        fetchVideo() {
 | 
					        fetchVideo() {
 | 
				
			||||||
            return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId());
 | 
					            return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/main.js
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								src/main.js
									
										
									
									
									
								
							| 
						 | 
					@ -25,7 +25,7 @@ library.add(
 | 
				
			||||||
    faHeadphones,
 | 
					    faHeadphones,
 | 
				
			||||||
    faYoutube,
 | 
					    faYoutube,
 | 
				
			||||||
    faRss,
 | 
					    faRss,
 | 
				
			||||||
    faChevronLeft
 | 
					    faChevronLeft,
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import("uikit/dist/css/uikit-core.css");
 | 
					import("uikit/dist/css/uikit-core.css");
 | 
				
			||||||
| 
						 | 
					@ -160,11 +160,25 @@ const mixin = {
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        urlify(string) {
 | 
					        urlify(string) {
 | 
				
			||||||
            const regex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
 | 
					            const regex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
 | 
				
			||||||
            if (!string) return '';
 | 
					            if (!string) return "";
 | 
				
			||||||
            return string.replace(regex, (url) => {
 | 
					            return string.replace(regex, url => {
 | 
				
			||||||
                return `<a class="uk-button uk-button-text" href="${url}" target="_blank">${url}</a>`
 | 
					                return `<a class="uk-button uk-button-text" href="${url}" target="_blank">${url}</a>`;
 | 
				
			||||||
            })
 | 
					            });
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        async updateWatched(videos) {
 | 
				
			||||||
 | 
					            if (window.db) {
 | 
				
			||||||
 | 
					                var tx = window.db.transaction("watch_history", "readonly");
 | 
				
			||||||
 | 
					                var store = tx.objectStore("watch_history");
 | 
				
			||||||
 | 
					                videos.map(async video => {
 | 
				
			||||||
 | 
					                    var request = store.get(video.url.substr(-11));
 | 
				
			||||||
 | 
					                    request.onsuccess = function(event) {
 | 
				
			||||||
 | 
					                        if (event.target.result) {
 | 
				
			||||||
 | 
					                            video.watched = true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    computed: {
 | 
				
			||||||
        backgroundColor() {
 | 
					        backgroundColor() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,11 @@ const routes = [
 | 
				
			||||||
        name: "Subscriptions",
 | 
					        name: "Subscriptions",
 | 
				
			||||||
        component: () => import("../components/SubscriptionsPage.vue"),
 | 
					        component: () => import("../components/SubscriptionsPage.vue"),
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: "/history",
 | 
				
			||||||
 | 
					        name: "Watch History",
 | 
				
			||||||
 | 
					        component: () => import("../components/HistoryPage.vue"),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = createRouter({
 | 
					const router = createRouter({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue