diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d7774c8f..67666d973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ##### 🚀 New Features -- Show error message if continue reading file does not exist +- Show error message if continue reading file does not exist [`7aee55c`](https://github.com/ollm/OpenComic/commit/7aee55ca5dac6b937824728b7ded116dc00c28df) +- Support background music from folder: MP3, M4A, WEBM, WEBA, OGG, OPUS, WAV, FLAC ##### 🐛 Bug Fixes diff --git a/README.md b/README.md index c11179075..75b055522 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ More [Screenshots 📸](https://github.com/ollm/OpenComic/blob/master/SCREENSHOT - 🖼 Support this image formats: `JPG`, `PNG`, `APNG`, `AVIF`, `WEBP`, `GIF`, `SVG`, `BMP`, `ICO` - 🗄 Support this compressed formats: `RAR`, `ZIP`, `7Z`, `TAR`, `CBR`, `CBZ`, `CB7`, `CBT` - 📄 Support this documents/ebook formats: `PDF`, `EPUB` (Alpha) +- 🎵 Support background music from folder: `MP3`, `M4A`, `WEBM`, `WEBA`, `OGG`, `OPUS`, `WAV`, `FLAC` - 📁 Master folders support - ☁️ Server connection support: `smb://`, `ftp://`, `ftps://`, `scp://`, `sftp://`, `ssh://` - 🇯🇵 Manga read mode diff --git a/languages/ca.json b/languages/ca.json index 82328b8f7..9a43ed331 100644 --- a/languages/ca.json +++ b/languages/ca.json @@ -169,6 +169,10 @@ "volumes": "Toms", "volume": "Tom" }, + "music": { + "main": "Música", + "volume": "Volum" + }, "moreOptions": { "main": "Més opcions", "hideBarHeader": "Amaga la barra superior", diff --git a/languages/cs.json b/languages/cs.json index 68f93ff12..74549186d 100644 --- a/languages/cs.json +++ b/languages/cs.json @@ -169,6 +169,10 @@ "volumes": "Svazky", "volume": "Svazek" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "Více nastavení", "hideBarHeader": "Skrýt vrchní panel", diff --git a/languages/de.json b/languages/de.json index f6f280a6c..05ae41996 100644 --- a/languages/de.json +++ b/languages/de.json @@ -169,6 +169,10 @@ "volumes": "Ausgaben", "volume": "Ausgabe" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/en.json b/languages/en.json index 6e2eed861..56d38f8a9 100644 --- a/languages/en.json +++ b/languages/en.json @@ -169,6 +169,10 @@ "volumes": "Volumes", "volume": "Volume" }, + "music": { + "main": "Music", + "volume": "Volume" + }, "moreOptions": { "main": "More options", "hideBarHeader": "Hide header bar", diff --git a/languages/es.json b/languages/es.json index 7374fb790..a8183bfaf 100644 --- a/languages/es.json +++ b/languages/es.json @@ -169,6 +169,10 @@ "volumes": "Tomos", "volume": "Tomo" }, + "music": { + "main": "Música", + "volume": "Volumen" + }, "moreOptions": { "main": "Más opciones", "hideBarHeader": "Ocultar barra superior", diff --git a/languages/fr.json b/languages/fr.json index 5837ee350..0ec6f3f74 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -169,6 +169,10 @@ "volumes": "Volumes", "volume": "Volume" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/hu.json b/languages/hu.json index 1be2b5e9a..5e35597e3 100644 --- a/languages/hu.json +++ b/languages/hu.json @@ -169,6 +169,10 @@ "volumes": "Volumes", "volume": "Volume" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/it.json b/languages/it.json index c5ac9d81e..96da8291d 100644 --- a/languages/it.json +++ b/languages/it.json @@ -169,6 +169,10 @@ "volumes": "", "volume": "" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/ja.json b/languages/ja.json index a3fe79a2d..8251a3306 100644 --- a/languages/ja.json +++ b/languages/ja.json @@ -169,6 +169,10 @@ "volumes": "巻", "volume": "巻" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/pt-br.json b/languages/pt-br.json index 31892cb87..2ea6a85dc 100644 --- a/languages/pt-br.json +++ b/languages/pt-br.json @@ -169,6 +169,10 @@ "volumes": "", "volume": "" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/languages/ru.json b/languages/ru.json index 913146eb5..2716c354e 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -169,6 +169,10 @@ "volumes": "Тома", "volume": "Том" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "Дополнительные параметры", "hideBarHeader": "Скрыть панель заголовка", diff --git a/languages/th.json b/languages/th.json index 3c8a576ee..a5c6f6491 100644 --- a/languages/th.json +++ b/languages/th.json @@ -169,6 +169,10 @@ "volumes": "เล่ม", "volume": "เล่ม" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "ตัวเลือกเพิ่มเติม", "hideBarHeader": "ซ่อนแถบส่วนบน", diff --git a/languages/vi.json b/languages/vi.json index cc559c279..d6f773f6f 100644 --- a/languages/vi.json +++ b/languages/vi.json @@ -169,6 +169,10 @@ "volumes": "Tập", "volume": "Tập" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "Thêm tùy chọn", "hideBarHeader": "Ẩn thanh công cụ", diff --git a/languages/zh-hans.json b/languages/zh-hans.json index 376627ff6..7855e9e27 100644 --- a/languages/zh-hans.json +++ b/languages/zh-hans.json @@ -169,6 +169,10 @@ "volumes": "卷", "volume": "卷" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "更多选项", "hideBarHeader": "隐藏标题栏", diff --git a/languages/zh-hant.json b/languages/zh-hant.json index 61432a04a..a6c27f30c 100644 --- a/languages/zh-hant.json +++ b/languages/zh-hant.json @@ -169,6 +169,10 @@ "volumes": "", "volume": "" }, + "music": { + "main": "", + "volume": "" + }, "moreOptions": { "main": "", "hideBarHeader": "", diff --git a/scripts/dom.js b/scripts/dom.js index 6243c2eb0..658bdf7f3 100644 --- a/scripts/dom.js +++ b/scripts/dom.js @@ -362,6 +362,7 @@ async function loadIndexPage(animation = true, path = false, content = false, ke onReading = _onReading = false; reading.hideContent(); + reading.music.pause(); generateAppMenu(); @@ -1904,6 +1905,15 @@ async function openComic(animation = true, path = true, mainPath = true, end = f // Show loadign page headerPath(path, mainPath); + // Load files + let file = fileManager.file(path); + let files = await file.read({filtered: false}); + + let hasMusic = await reading.music.has(files); + handlebarsContext.hasMusic = hasMusic; + + files = fileManager.filtered(files); + handlebarsContext.comics = []; if(fromDeepLoad && Date.now() - fromDeepLoadNow < 200) @@ -1923,14 +1933,12 @@ async function openComic(animation = true, path = true, mainPath = true, end = f template.loadContentLeft('reading.content.left.html', animation); - // Load files - let file = fileManager.file(path); - let files = await file.read(); - let isCanvas = false; let isEbook = false; let compressedFile = fileManager.lastCompressedFile(path); + if(hasMusic) files.push(hasMusic); // Only to make available + if(compressedFile) { let features = fileManager.fileCompressed(compressedFile); @@ -1957,6 +1965,8 @@ async function openComic(animation = true, path = true, mainPath = true, end = f await file.makeAvailable(files, false, true); } + if(hasMusic) files.pop(); // Remove now + file.destroy(); skipNextComic = await nextComic(path, mainPath); @@ -2091,6 +2101,7 @@ async function openComic(animation = true, path = true, mainPath = true, end = f reading.read(path, indexStart, end, isCanvas, isEbook, imagePath); reading.hideContent(electronRemote.getCurrentWindow().isFullScreen(), true); + reading.music.read(hasMusic); generateAppMenu(); diff --git a/scripts/file-manager.js b/scripts/file-manager.js index 07b5b0b96..90d21c737 100644 --- a/scripts/file-manager.js +++ b/scripts/file-manager.js @@ -471,7 +471,7 @@ var file = function(path, config = false) { let name = p.parse(path).name; - let regex = new RegExp('^(?:[\-\s0-9+])?(?:'+pregQuote(name)+(inside ? '|cover|default|folder|series|poster' : '')+')(?:[\-\s0-9+])?\.[a-z0-9]+$'); + let regex = new RegExp('^(?:[\-\s0-9+])?(?:'+pregQuote(name)+'(?:[_-]?(?:cover|default|folder|series|poster|thumbnail))?'+(inside ? '|cover|default|folder|series|poster|thumbnail' : '')+')(?:[\-\s0-9+])?\.[a-z0-9]+$'); let poster = false; let len = files.length diff --git a/scripts/gamepad.js b/scripts/gamepad.js index 55011be74..7d98d6cf4 100644 --- a/scripts/gamepad.js +++ b/scripts/gamepad.js @@ -704,6 +704,8 @@ function showMenu() else events.activeMenu(query, false, 'gamepad'); + dom.query('.gamepad-reading-music').css({display: !handlebarsContext.hasMusic ? 'none' : ''}); + if(!onReading) { let viewIcon = document.querySelector('.menu-gamepad-view-icon'); diff --git a/scripts/opencomic.js b/scripts/opencomic.js index 67f243c25..7bfff6a0f 100644 --- a/scripts/opencomic.js +++ b/scripts/opencomic.js @@ -321,6 +321,19 @@ var compatibleSpecialExtensions = [ 'tbn', ]; +var audioExtensions = { + all: [ + 'mp3', + 'm4a', + 'webm', + 'weba', + 'ogg', + 'opus', + 'wav', + 'flac', + ], +}; + //console.time('Require time 2'); const app = require(p.join(appDir, 'scripts/app.js')), diff --git a/scripts/reading.js b/scripts/reading.js index b7414b096..d71fcfa3e 100644 --- a/scripts/reading.js +++ b/scripts/reading.js @@ -1,5 +1,6 @@ const render = require(p.join(appDir, 'scripts/reading/render.js')), filters = require(p.join(appDir, 'scripts/reading/filters.js')), + music = require(p.join(appDir, 'scripts/reading/music.js')), readingEbook = require(p.join(appDir, 'scripts/reading/ebook.js')); var images = {}, imagesData = {}, imagesDataClip = {}, imagesPath = {}, imagesNum = 0, contentNum = 0, imagesNumLoad = 0, currentIndex = 1, imagesPosition = {}, imagesFullPosition = {}, foldersPosition = {}, indexNum = 0, imagesDistribution = [], currentPageXY = {x: 0, y: 0}, currentMousePosition = {pageX: 0, pageY: 0}; @@ -2103,7 +2104,7 @@ function fixBlurOnZoom(scale = 1, index = false) img.style.width = (_width / window.devicePixelRatio)+'px'; img.style.height = (_height / window.devicePixelRatio)+'px'; - if(img.classList.contains('blobRender')) + if(img.classList.contains('blobRender') || img.classList.contains('zoomOriginalSize') || img.classList.contains('originalSize')) img.style.transform = 'scale('+_scale+') translate(0.001px, 0.001px)'; else img.style.transform = 'scale('+_scale+')'; @@ -2120,7 +2121,7 @@ function fixBlurOnZoom(scale = 1, index = false) { let img = images[i]; - if(!img.classList.contains('blobRender')) + if(!img.classList.contains('blobRender') && !img.classList.contains('zoomOriginalSize') && !img.classList.contains('originalSize')) { let rect = img.getBoundingClientRect(); @@ -5100,5 +5101,6 @@ module.exports = { onLoad: onLoad, ebook: readingEbook, filters: filters, + music: music, render: render, }; \ No newline at end of file diff --git a/scripts/reading/music.js b/scripts/reading/music.js new file mode 100644 index 000000000..0c13ae6da --- /dev/null +++ b/scripts/reading/music.js @@ -0,0 +1,114 @@ + + + + + + + +let current = false, audio = false; + +async function has(files) +{ + for(let i = 0, len = files.length; i < len; i++) + { + let _file = files[i]; + + if(inArray(fileExtension(_file.name), audioExtensions.all)) + return _file; + } + + return false; +} + +async function read(file) +{ + current = file; + + if(file) + { + generate(); + + if(config.readingMusic.play) + play(); + } + else + { + pause(); + } +} + +function generate() +{ + let globalElement = template._globalElement(); + + audio = globalElement.querySelector('.reading-music'); + + if(!audio) + { + audio = document.createElement('audio'); + audio.className = 'reading-music'; + audio.autoplay = false; + audio.controls = false; + audio.loop = true; + audio.style.display = 'none'; + audio.volume = config.readingMusic.volume; + globalElement.appendChild(audio); + } + + audio.src = fileManager.realPath(current.path); + + current = audio; +} + +function play() +{ + if(!audio) return; + audio.play(); +} + +function pause() +{ + if(!audio) return; + audio.pause(); +} + +function volume(volume, save = false) +{ + volume /= 100; + + config.readingMusic.volume = volume; + if(save) storage.setVar('config', 'readingMusic', config.readingMusic); + + if(!audio) return; + audio.volume = volume; +} + +function setPlay(_play = true) +{ + config.readingMusic.play = _play; + storage.setVar('config', 'readingMusic', config.readingMusic); + + if(_play) + play(); + else + pause(); +} + +function loadMenu() +{ + handlebarsContext.volumePercent = Math.round(config.readingMusic.volume * 100); + + dom.query('#reading-music .menu-simple').html(template.load('reading.elements.menus.music.html')); + + events.events(); +} + +module.exports = { + has: has, + read: read, + play: play, + pause: pause, + volume: volume, + setPlay: setPlay, + loadMenu: loadMenu, +}; \ No newline at end of file diff --git a/scripts/storage.js b/scripts/storage.js index 0c78466b6..13814a2bd 100644 --- a/scripts/storage.js +++ b/scripts/storage.js @@ -1,4 +1,4 @@ -var changes = 75; // Update this if readingPagesConfig is updated +var changes = 76; // Update this if readingPagesConfig is updated var readingPagesConfig = { readingConfigName: '', @@ -118,6 +118,10 @@ var storageDefault = { readingTrackingAtTheEnd: true, readingImageInterpolationMethodDownscaling: 'lanczos3', readingImageInterpolationMethodUpscaling: 'chromium', + readingMusic: { + play: true, + volume: 1.0, + }, controllerDeadZone: 0.06, startInFullScreen: false, startInContinueReading: false, diff --git a/templates/reading.elements.menus.html b/templates/reading.elements.menus.html index 0dd029e45..9a0012114 100644 --- a/templates/reading.elements.menus.html +++ b/templates/reading.elements.menus.html @@ -96,6 +96,10 @@ +