diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 9f983e2e..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 -updates: - # Maintain dependencies for npm - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "daily" - # Maintain dependencies for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" diff --git a/package.json b/package.json index 06dfcfa2..fee5212b 100644 --- a/package.json +++ b/package.json @@ -9,42 +9,41 @@ "lint": "eslint --fix --color --ignore-path .gitignore --ext .js,.vue ." }, "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.2.0", - "@fortawesome/free-brands-svg-icons": "^6.2.0", - "@fortawesome/free-solid-svg-icons": "^6.2.0", - "@fortawesome/vue-fontawesome": "^3.0.1", - "buffer": "^6.0.3", - "dompurify": "^2.4.0", - "hotkeys-js": "^3.10.0", - "javascript-time-ago": "^2.5.7", - "mux.js": "^6.2.0", - "shaka-player": "4.2.2", - "stream": "^0.0.2", - "vue": "^3.2.41", - "vue-i18n": "^9.2.2", - "vue-router": "^4.1.5", - "xml-js": "^1.6.11" + "@fortawesome/fontawesome-svg-core": "6.2.0", + "@fortawesome/free-brands-svg-icons": "6.2.0", + "@fortawesome/free-solid-svg-icons": "6.2.0", + "@fortawesome/vue-fontawesome": "3.0.2", + "buffer": "6.0.3", + "dompurify": "2.4.0", + "hotkeys-js": "3.10.0", + "javascript-time-ago": "2.5.9", + "mux.js": "6.2.0", + "shaka-player": "4.2.3", + "stream-browserify": "3.0.0", + "vue": "3.2.43", + "vue-i18n": "9.2.2", + "vue-router": "4.1.6", + "xml-js": "1.6.11" }, "devDependencies": { - "@iconify/json": "^2.1.122", - "@intlify/vite-plugin-vue-i18n": "^6.0.3", - "@unocss/preset-icons": "^0.45.25", - "@unocss/preset-web-fonts": "^0.45.25", - "@unocss/transformer-directives": "^0.45.26", - "@unocss/transformer-variant-group": "^0.45.25", - "@vitejs/plugin-legacy": "^1.8.2", - "@vitejs/plugin-vue": "^2.3.4", - "@vue/compiler-sfc": "3.2.41", - "babel-eslint": "^10.1.0", - "eslint": "^7.32.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-vue": "^9.6.0", - "prettier": "^2.7.1", - "unocss": "^0.45.29", - "vite": "^2.9.14", - "vite-plugin-eslint": "^1.8.1", - "vite-plugin-pwa": "^0.12.8" + "@iconify/json": "2.1.135", + "@intlify/vite-plugin-vue-i18n": "6.0.3", + "@unocss/preset-icons": "0.46.4", + "@unocss/preset-web-fonts": "0.46.4", + "@unocss/transformer-directives": "0.46.4", + "@unocss/transformer-variant-group": "0.46.4", + "@vitejs/plugin-legacy": "2.3.1", + "@vitejs/plugin-vue": "3.2.0", + "@vue/compiler-sfc": "3.2.43", + "eslint": "8.27.0", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-vue": "9.7.0", + "prettier": "2.7.1", + "unocss": "0.46.4", + "vite": "3.2.3", + "vite-plugin-eslint": "1.8.1", + "vite-plugin-pwa": "0.13.3" }, "eslintConfig": { "root": true, @@ -56,9 +55,6 @@ "plugin:prettier/recommended", "eslint:recommended" ], - "parserOptions": { - "parser": "babel-eslint" - }, "rules": {} }, "browserslist": [ diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..05722b80 --- /dev/null +++ b/renovate.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + "group:recommended" + ], + "ignorePresets": [ + ":prHourlyLimit2" + ], + "packageRules": [ + { + "matchPackagePrefixes": [ + "@unocss/" + ], + "matchPackageNames": [ + "unocss" + ], + "groupName": "unocss" + } + ] +} diff --git a/src/App.vue b/src/App.vue index 8346bd15..fcfd52b3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -42,8 +42,8 @@ export default { }); if (this.getPreferenceBoolean("watchHistory", false) || this.getPreferenceBoolean("hideWatched", false)) { if ("indexedDB" in window) { - const request = indexedDB.open("piped-db", 1); - request.onupgradeneeded = function () { + const request = indexedDB.open("piped-db", 2); + request.onupgradeneeded = ev => { const db = request.result; console.log("Upgrading object store."); if (!db.objectStoreNames.contains("watch_history")) { @@ -51,6 +51,10 @@ export default { store.createIndex("video_id_idx", "videoId", { unique: true }); store.createIndex("id_idx", "id", { unique: true, autoIncrement: true }); } + if (ev.oldVersion < 2) { + const store = request.transaction.objectStore("watch_history"); + store.createIndex("watchedAt", "watchedAt", { unique: false }); + } }; request.onsuccess = e => { window.db = e.target.result; @@ -63,14 +67,16 @@ export default { const App = this; (async function () { - const defaultLang = await App.defaultLangage; + const defaultLang = await App.defaultLanguage; const locale = App.getPreferenceString("hl", defaultLang); if (locale !== App.TimeAgoConfig.locale) { - const localeTime = await import( - "./../node_modules/javascript-time-ago/locale/" + locale + ".json" - ).then(module => module.default); - App.TimeAgo.addLocale(localeTime); - App.TimeAgoConfig.locale = locale; + const localeTime = await import(`../node_modules/javascript-time-ago/locale/${locale}.json`) + .catch(() => null) + .then(module => module?.default); + if (localeTime) { + App.TimeAgo.addLocale(localeTime); + App.TimeAgoConfig.locale = locale; + } } if (window.i18n.global.locale.value !== locale) { if (!window.i18n.global.availableLocales.includes(locale)) { diff --git a/src/components/ChannelItem.vue b/src/components/ChannelItem.vue new file mode 100644 index 00000000..84557629 --- /dev/null +++ b/src/components/ChannelItem.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/ChannelPage.vue b/src/components/ChannelPage.vue index d08ce459..1ef8a400 100644 --- a/src/components/ChannelPage.vue +++ b/src/components/ChannelPage.vue @@ -30,18 +30,32 @@ v-if="channel.id" :href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`" target="_blank" - class="btn flex-col ml-3" + class="btn flex-col mx-3" > + + +
+ +
+
- import ErrorHandler from "./ErrorHandler.vue"; -import VideoItem from "./VideoItem.vue"; +import ContentItem from "./ContentItem.vue"; +import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; export default { components: { ErrorHandler, - VideoItem, + ContentItem, + WatchOnYouTubeButton, }, data() { return { channel: null, subscribed: false, + tabs: [], + selectedTab: 0, + contentItems: [], }; }, mounted() { @@ -111,25 +130,58 @@ export default { .then(() => { if (!this.channel.error) { document.title = this.channel.name + " - Piped"; + this.contentItems = this.channel.relatedStreams; this.fetchSubscribedStatus(); this.updateWatched(this.channel.relatedStreams); + this.tabs.push({ + translatedName: this.$t("video.videos"), + }); + for (let i = 0; i < this.channel.tabs.length; i++) { + let tab = this.channel.tabs[i]; + tab.translatedName = this.getTranslatedTabName(tab.name); + this.tabs.push(tab); + } } }); }, handleScroll() { - if (this.loading || !this.channel || !this.channel.nextpage) return; + if ( + this.loading || + !this.channel || + !this.channel.nextpage || + (this.selectedTab != 0 && !this.tabs[this.selectedTab].tabNextPage) + ) + return; if (window.innerHeight + window.scrollY >= document.body.offsetHeight - window.innerHeight) { this.loading = true; - this.fetchJson(this.apiUrl() + "/nextpage/channel/" + this.channel.id, { - nextpage: this.channel.nextpage, - }).then(json => { - this.channel.nextpage = json.nextpage; - this.loading = false; - this.updateWatched(json.relatedStreams); - json.relatedStreams.map(stream => this.channel.relatedStreams.push(stream)); - }); + if (this.selectedTab == 0) { + this.fetchChannelNextPage(); + } else { + this.fetchChannelTabNextPage(); + } } }, + fetchChannelNextPage() { + this.fetchJson(this.apiUrl() + "/nextpage/channel/" + this.channel.id, { + nextpage: this.channel.nextpage, + }).then(json => { + this.channel.nextpage = json.nextpage; + this.loading = false; + this.updateWatched(json.relatedStreams); + json.relatedStreams.map(stream => this.contentItems.push(stream)); + }); + }, + fetchChannelTabNextPage() { + this.fetchJson(this.apiUrl() + "/channels/tabs", { + data: this.tabs[this.selectedTab].data, + nextpage: this.tabs[this.selectedTab].tabNextPage, + }).then(json => { + this.tabs[this.selectedTab].tabNextPage = json.nextpage; + this.loading = false; + json.content.map(item => this.contentItems.push(item)); + this.tabs[this.selectedTab].content = this.contentItems; + }); + }, subscribeHandler() { if (this.authenticated) { this.fetchJson(this.authApiUrl() + (this.subscribed ? "/unsubscribe" : "/subscribe"), null, { @@ -147,6 +199,50 @@ export default { } this.subscribed = !this.subscribed; }, + getTranslatedTabName(tabName) { + let translatedTabName = tabName; + switch (tabName) { + case "Livestreams": + translatedTabName = this.$t("titles.livestreams"); + break; + case "Playlists": + translatedTabName = this.$t("titles.playlists"); + break; + case "Channels": + translatedTabName = this.$t("titles.channels"); + break; + case "Shorts": + translatedTabName = this.$t("video.shorts"); + break; + default: + console.error(`Tab name "${tabName}" is not translated yet!`); + break; + } + return translatedTabName; + }, + loadTab(index) { + this.selectedTab = index; + if (index == 0) { + this.contentItems = this.channel.relatedStreams; + return; + } + if (this.tabs[index].content) { + this.contentItems = this.tabs[index].content; + return; + } + this.fetchJson(this.apiUrl() + "/channels/tabs", { + data: this.tabs[index].data, + }).then(tab => { + this.contentItems = this.tabs[index].content = tab.content; + this.tabs[this.selectedTab].tabNextPage = tab.nextpage; + }); + }, }, }; + + diff --git a/src/components/CommentItem.vue b/src/components/CommentItem.vue index 3509ed17..f1caec1a 100644 --- a/src/components/CommentItem.vue +++ b/src/components/CommentItem.vue @@ -29,7 +29,7 @@
- @@ -32,7 +33,7 @@ import ErrorHandler from "./ErrorHandler.vue"; import VideoItem from "./VideoItem.vue"; +import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; export default { components: { ErrorHandler, VideoItem, + WatchOnYouTubeButton, }, data() { return { diff --git a/src/components/PlaylistVideos.vue b/src/components/PlaylistVideos.vue index c8374e4d..f8a118e2 100644 --- a/src/components/PlaylistVideos.vue +++ b/src/components/PlaylistVideos.vue @@ -3,7 +3,7 @@ -
diff --git a/src/components/VideoPlayer.vue b/src/components/VideoPlayer.vue index 74520f64..9162dc4f 100644 --- a/src/components/VideoPlayer.vue +++ b/src/components/VideoPlayer.vue @@ -12,8 +12,11 @@ + + diff --git a/src/components/WatchVideo.vue b/src/components/WatchVideo.vue index d5312ef7..91c95f1f 100644 --- a/src/components/WatchVideo.vue +++ b/src/components/WatchVideo.vue @@ -47,11 +47,11 @@